Skip to content

Commit

Permalink
publisher: rewrite, release note support
Browse files Browse the repository at this point in the history
  • Loading branch information
dady8889 committed Jul 22, 2021
1 parent de574b5 commit 355e7f5
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 38 deletions.
10 changes: 8 additions & 2 deletions Onova.Publisher.nuspec
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Onova.Publisher</id>
<title>Onova.Publisher</title>
<version>1.0.0</version>
<version>1.1.0</version>
<authors>dady8889</authors>
<owners>dady8889</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Provides publishing support for applications using Onova.</description>
<projectUrl>https://github.com/dady8889/Onova.Publisher</projectUrl>
<license type="expression">MIT</license>
<tags>onova installer publisher updater tool</tags>
<dependencies>
<group targetFramework="net5.0" />
<group targetFramework="net6.0" />
</dependencies>
<releaseNotes>
* Rewrite command line app
* Implement generation of release note files for Onova.ReleaseNotes, can disable with --no-rn
* More command aliases and better help
</releaseNotes>
</metadata>
<files>
<file src="Onova.Publisher\publish\**" target="tools/"/>
Expand Down
14 changes: 13 additions & 1 deletion Onova.Publisher/Onova.Publisher.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,27 @@
<TargetFramework>net5.0</TargetFramework>
<Authors>dady8889</Authors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<AssemblyVersion>1.1.0.0</AssemblyVersion>
<FileVersion>1.1.0.0</FileVersion>
<PackageReleaseNotes>* Rewrite command line app
* Implement generation of release note files for Onova.ReleaseNotes, can disable with --no-rn
* More command aliases and better help
</PackageReleaseNotes>
<Version>1.1.0</Version>
</PropertyGroup>

<ItemGroup>
<Compile Remove="publish\**" />
<EmbeddedResource Remove="publish\**" />
<None Remove="publish\**" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="NuGet.CommandLine" Version="5.9.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.21308.1" />
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.3.0-alpha.21216.1" />
<PackageReference Include="System.CommandLine.Rendering" Version="0.3.0-alpha.21216.1" />
</ItemGroup>

Expand Down
80 changes: 61 additions & 19 deletions Onova.Publisher/Program.cs
Original file line number Diff line number Diff line change
@@ -1,66 +1,107 @@
using System;
using System.IO;
using System.CommandLine;
using System.CommandLine.Invocation;

namespace Onova.Publisher
{
class Program
{
/// <summary>
/// Publishes your application for Onova.
/// </summary>
/// <param name="name">Your application's name (name of executable without extension). Maximum 64 characters.</param>
/// <param name="version">Version in format major.minor[.build[.revision]].</param>
/// <param name="url">URL to web where the manifest resides. Maximum 1024 characters.</param>
/// <param name="target">Folder which will get packed into a zip.</param>
/// <param name="output">Output folder which will contain the publish folder. Publish folder will contain the updated manifest file, zip and installer.</param>
static int Main(string name, string version, string url, string target, string output = ".")

static int Main(string[] args)
{
if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(version) ||
string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(target) ||
var rootCommand = new RootCommand
{
new Option<string>(
new [] { "--name", "-n" },
"Your application's name (name of executable without extension). Maximum 64 characters.")
{ IsRequired = false },

new Option<string>(
new [] { "--version", "-v" },
"Version in format major.minor[.build[.revision]].")
{ IsRequired = false },

new Option<string>(
new [] { "--url", "-u" },
"URL to web where the manifest resides. Maximum 1024 characters.")
{ IsRequired = false },

new Option<string>(
new [] { "--target", "--in", "-i" },
"Folder which will get packed into a zip.")
{ IsRequired = false },

new Option<string>(
new [] {"--output", "--out", "-o" },
() => ".",
"Output folder which will contain the publish folder. Publish folder will contain the updated manifest file, zip and installer.")
{ IsRequired = false },

new Option<bool>(
new [] {"--no-releasenotes", "--no-rn"},
"Disables generation of an empty release note (.rn) file.")
{ IsRequired = false }
};

rootCommand.Description = "Publishes your application for Onova.";
rootCommand.Handler = CommandHandler.Create<string, string, string, string, string, bool>(CommandLineHandler);

return rootCommand.InvokeAsync(args).Result;
}

static int CommandLineHandler(string name, string version, string url, string target, string output, bool noReleaseNotes)
{
if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(version) ||
string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(target) ||
string.IsNullOrWhiteSpace(output))
{
Console.WriteLine("Required parameter is missing.");
Console.Error.WriteLine("Missing a required option. Try running Onova.Publisher -h to get more information.");
return 1;
}

if (name.Length > InstallerConstant.AppNameLength)
{
Console.WriteLine("App name is invalid.");
Console.Error.WriteLine("App name is invalid.");
return 1;
}

if (!Version.TryParse(version, out _))
{
Console.WriteLine("Version is invalid.");
Console.Error.WriteLine("Version is invalid.");
return 1;
}

if (url.Length > InstallerConstant.ManifestUrlLength)
{
Console.WriteLine("Manifest URL is invalid.");

Console.Error.WriteLine("Manifest URL is invalid.");
return 1;
}

if (!Directory.Exists(target))
{
Console.WriteLine("Target folder is invalid.");
Console.Error.WriteLine("Target folder is invalid.");
return 1;
}

if (!Directory.Exists(output))
{
Console.WriteLine("Output folder is invalid.");
Console.Error.WriteLine("Output folder is invalid.");
return 1;
}

if (!File.Exists(Path.Combine(target, name + ".exe")))
{
Console.WriteLine("Target folder does not contain the executable. Check that the target name is the actual name of the executable.");
Console.Error.WriteLine("Target folder does not contain the executable. Check that the target name is the actual name of the executable.");
return 1;
}

var publisher = new Publisher(name, version, url, target, output);

if (noReleaseNotes)
publisher.DisableReleaseNotes();

try
{
if (publisher.CheckVersionPublished())
Expand All @@ -69,10 +110,11 @@ static int Main(string name, string version, string url, string target, string o
publisher.CreateZip();
publisher.RebuildManifest();
publisher.CreateInstaller();
publisher.CreateEmptyReleaseNote();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.Error.WriteLine(ex.Message);
return 1;
}

Expand Down
39 changes: 37 additions & 2 deletions Onova.Publisher/Publisher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal class Publisher

public string PublisherDirectory { get; }
public string InstallerPath { get; }

public bool GenerateReleaseNotes { get; private set; }

public Publisher(string appName, string appVersion, string manifestUrl, string targetFolder, string outputFolder)
{
Expand All @@ -44,6 +46,8 @@ public Publisher(string appName, string appVersion, string manifestUrl, string t

PublisherDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
InstallerPath = Path.Combine(PublisherDirectory, InstallerConstant.InstallerName);

GenerateReleaseNotes = true;
}

public bool CheckVersionPublished()
Expand Down Expand Up @@ -111,9 +115,14 @@ private void AppendManifest(TextWriter writer, string appVersion, string fileNam
}
}

private string ReleasifyName(string name, string version)
private string ReleasifyName(string name, string version, bool withExtension = true)
{
return $"{name.Replace(' ', '-')}-{version}.zip";
string relName = $"{name.Replace(' ', '-')}-{version}";

if (withExtension)
relName += ".zip";

return relName;
}

public void CreateInstaller()
Expand Down Expand Up @@ -149,5 +158,31 @@ public void CreateInstaller()
bw.Write(packedReserved);
}
}

public void DisableReleaseNotes()
{
GenerateReleaseNotes = false;
}

public void CreateEmptyReleaseNote()
{
if (!GenerateReleaseNotes)
return;

try
{
var rnPath = Path.Combine(ReleaseFolder, ReleasifyName(AppName, AppVersion, false) + ".rn");

// if the rn file already exists, just skip
if (File.Exists(rnPath))
return;

File.Create(rnPath);
}
catch (Exception ex)
{
throw new Exception("Cannot create release note file.", ex);
}
}
}
}
33 changes: 19 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The basis of this project is the great [Onova update library](https://github.com
However, Onova is unaware of how the application got installed on the computer.
This fact can be an upside for some, but having different installer/updater can be cumbersome.

# Introduction
## Introduction

**Onova.Publisher is a tool that allows you to create new packages directly from Visual Studio Package Manager Console.**
The package will be targeted for the [Onova WebPackageResolver](https://github.com/Tyrrrz/Onova#webpackageresolver).
Expand All @@ -20,7 +20,7 @@ The most important part is the installer - in comparison to Squirrel, which pack

Enough talking, let us show you what we got.

# Documentation
## Documentation
The only **requirement** for Onova.Publisher to work is that your project **targets .NET 5** *(actually, it may work for other .NET Core versions but I have not tested it)*.

Let us assume you have built and dotnet published your app into the *\publish* folder of your solution.
Expand All @@ -36,11 +36,13 @@ Usage:
Onova.Publisher [options]
Options:
--name <name> Your application's name (name of executable without extension). Maximum 64 characters.
--version <version> Version in format major.minor[.build[.revision]].
--url <url> URL to web where the manifest resides. Maximum 1024 characters.
--target <target> Folder which will get packed into a zip.
--output <output> Output folder which will contain the publish folder. Publish folder will contain the updated manifest file, zip and installer. [default: .]
-n, --name <name> Your application's name (name of executable without extension). Maximum 64 characters.
-v, --version <version> Version in format major.minor[.build[.revision]].
-u, --url <url> URL to web where the manifest resides. Maximum 1024 characters.
-i, --in, --target <target> Folder which will get packed into a zip.
-o, --out, --output <output> Output folder which will contain the publish folder. Publish folder will contain the updated manifest file, zip and installer. [default: .]
--no-releasenotes, --no-rn Disables generation of an empty release note (.rn) file.
-?, -h, --help Show help and usage information
```

After reading the documentation, let's try publishing our DummyApp version 1.2.3 to our web server at https://dummy.com/files/.
Expand All @@ -58,10 +60,13 @@ The contents of the folder are following:
```
MANIFEST
DummyApp-1.2.3.zip
DummyApp-1.2.3.rn
websetup.exe
```
The MANIFEST file will contain one line, *1.2.3 DummyApp-1.2.3.zip*. This file is precisely formatted for the [Onova WebPackageResolver](https://github.com/Tyrrrz/Onova#webpackageresolver).

The .rn file is an empty text file, in which you can add release notes for your release. For more information, check out [Onova.ReleaseNotes](https://github.com/dady8889/Onova.ReleaseNotes).

You can now upload the files to the desired location. We will assume you have implemented the updating in your application like this:
```csharp
using (var mgr = new UpdateManager(new WebPackageResolver("https://dummy.com/files/MANIFEST"), new ZipPackageExtractor()))
Expand Down Expand Up @@ -104,30 +109,30 @@ Currently, the installed app **will not** be started after the installation.

If you want to uninstall the application, you can use the standard Windows uninstall procedure using Settings, or you can directly execute the uninstall.exe in the application base directory. *Also, in comparison with Squirrel, your application will have an icon visible in the installed programs menu, and the uninstaller leaves no installed files behind.*

# Missing (planned) features
- [ ] Changelog support
## Missing (planned) features
- [x] Changelog support
- [ ] Code signing and timestamping
- [ ] Start menu link in publisher name folder
- [ ] Run app after installation
- [ ] Powershell bindings
- [ ] Silent install

# Missing (not planned) features
## Missing (not planned) features
* Bigger customization eg. icons, custom UI
* Dedicated non-web installer
* Other OS support or backporting to older .NET versions
* Bootstrapping

# Known issues
## Known issues
- [ ] No wiki
- [ ] No tests
- [ ] App name and URL allows only ANSI encoding
- [ ] The installer code is a bit inconsistent

# Contributions
## Contributions
I am open to suggestions, PRs, bug reports.
Any contribution is welcome.

# Licensing
## Licensing
The project uses following licensed works:

* unzip.h unzip.cpp - Lucian Wischik, Jean-Loup Gailly, Mark Adler, zlib
Expand Down

0 comments on commit 355e7f5

Please sign in to comment.