NetSparkleUpdater/NetSparkle

Changelog makes appcast invalid

christophwille opened this issue · 16 comments

Error:

netsparkle: Downloading app cast signature data...
netsparkle: Appcast is valid! Parsing...
netsparkle: While parsing app cast item, converted 'Sat, 03 Jun 2023 16:28:52 +02:00' to 6/3/2023 4:28:52 PM.
netsparkle: Found an item in the app cast: version 8.0.0.7400 (8.0.0) -- os = windows
netsparkle: App cast successfully downloaded and parsed. Getting available updates...
netsparkle: Couldn't read/parse the app cast: Value cannot be null. (Parameter 'input')
netsparkle: No version information in app cast found

Command:

netsparkle-generate-appcast -n "ILSpy" -a ./output -e msi -b ./ -o windows -f true -u https://github.com/icsharpcode/ILSpy/releases/tag/v8.0 --description-tag ".NET Decompiler" --key-path ./signingkeys --change-log-path ./

appcast.xml generated:

<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"><channel><title>ILSpy</title><description>.NET Decompiler</description><language>en</language><item><title>ILSpy 8.0.0.7400</title><description>* feature 1
* noteable 2
* something 3</description><pubDate>Sat, 03 Jun 2023 16:28:52 +02:00</pubDate><enclosure url="https://github.com/icsharpcode/ILSpy/releases/tag/v8.0/ILSpy_Installer_8.0.0.7400.msi" sparkle:version="8.0.0.7400" sparkle:shortVersionString="8.0.0" length="4456448" sparkle:os="windows" type="application/octet-stream" sparkle:signature="921UgLifNi8t3VTJCeMGZtQGPLm+NsmkyY3wr5zdPKPektSEKYxgmIzAev48RcJXw7htOZPD6q7zAYgcjS0HDA==" /></item></channel></rss>

Original md file:

* feature 1
* noteable 2
* something 3

EDIT: Because that might make a difference - I am working on a Windows machine, and of course my changelog md has crlf for line endings.

Hmmm...I'm having a hard time replicating this one on my Windows machine. It doesn't appear to be the app cast itself, as that is parsing OK. Tried it with your app cast that you sent and with using the app cast URL from a previous issue (and did check it with CLRF, thanks for noting that).

image

Decided to push out a preview version with e.StackTrace going into the log too for easier debugging, so if you can update and try that to see more details on the error, that'd be great. If not, can you use one of the sample projects with your app cast URL and stick a breakpoint at line 645 of SparkleUpdater? (The line that starts LogWriter.PrintMessage("Couldn't read/parse...) Need the stack trace to see where this is breaking. I'm wondering if the Configuration object has no version information...?

(The No version information in app cast found is just a red herring; need to refine that error message to be more specific.)

ok, will try asap once the version shows up (I am currently on https://www.nuget.org/packages/NetSparkleUpdater.UI.WPF/2.3.0-preview20230604001)

What version of .NET is your sample for? The ILSpy version I am testing with is .NET 6.0.

I am now getting (didn't get that error when creating the issue)

netsparkle: Downloading app cast signature data...
netsparkle: Signature check of appcast failed
netsparkle: Appcast is not valid
netsparkle: No version information in app cast found

For good measure I re-created the appcast at https://ilspy.net/appcast.xml - my code hasn't really changed:

			string sparkleSettingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ICSharpCode\\ILSpy-sparkle.json");

			_sparkle = new SparkleUpdater(
				"https://ilspy.net/appcast.xml",
				new Ed25519Checker(SecurityMode.Strict, "s2P6MPexSRSjod77aWUjgVKj/gKYYAeqgHY/0Gf8b78=")
			) {
				UIFactory = new NetSparkleUpdater.UI.WPF.UIFactory(null),
				RelaunchAfterUpdate = false,
				CustomInstallerArguments = "",
				Configuration = new JSONConfiguration(null, sparkleSettingsPath)
			};
			_sparkle.StartLoop(true);

I'm wondering if the Configuration object has no version information...?

Were you refering to the JSONConfiguration above?

What version of .NET is your sample for? The ILSpy version I am testing with is .NET 6.0.

.NET 7. Good to know you're testing .NET 6 as that will help debug.

Were you refering to the JSONConfiguration above?

Yeah. At the top of XMLAppCast in GetAvailableUpdates() there is a Version installed = new Version(_config.InstalledVersion); call. I think the fact that you aren't sending an IAssemblyAccessor to your JSONConfiguration is what is breaking things. (Yes, that means the lib should probably throw/show an error if there is no IAssemblyAccessor so that the dev knows what's going on.)

Try new JSONConfiguration(new AssemblyReflectionAccessor(null /* assemblyName is not required and will use the entry assembly by default */), sparkleSettingsPath).

I won't have time to debug/make changes until this evening my time (EDT) at the earliest, so this might get you moving forward before I have more time.

I put

            _sparkle = new SparkleUpdater(
                "https://ilspy.net/appcast.xml",
                new Ed25519Checker(SecurityMode.Strict, "s2P6MPexSRSjod77aWUjgVKj/gKYYAeqgHY/0Gf8b78=")
            )
            {
                UIFactory = new NetSparkleUpdater.UI.WPF.UIFactory(null),
                RelaunchAfterUpdate = false,
                CustomInstallerArguments = ""
            };

            _sparkle.StartLoop(true, true);

into NetSparkleUpdater.Samples.NetCore.WPF - I see the same problems there

netsparkle: Downloading app cast signature data...
netsparkle: Signature check of appcast failed
netsparkle: Appcast is not valid
netsparkle: No version information in app cast found

This is for develop latest as of now christophwille@0561653

Did you upload the new .signature file for the updated app cast you put up? (Hence the Signature check of appcast failed error.)

I am currently stepping through the calls - there is some strange async code:

https://github.com/christophwille/NetSparkle/blob/0561653b064dc19e9e042530fba5b09794142558/src/NetSparkle/Downloaders/WebRequestAppCastDataDownloader.cs#L116

Everywhere else there seems to be a .Wait() - which isn't great either, it should be GetAwaiter().GetResult(). Even better would be one fully async method public async Task<string> DownloadAndGetAppCastData(string url) that is being GetAwaiter().GetResult()-ed in the existing method.

ok, here is the kicker - I extracted the code for sig validation and ran it directly against the files generated by the tool:

            var signatureVerifier = new Ed25519Checker(SecurityMode.Strict, "s2P6MPexSRSjod77aWUjgVKj/gKYYAeqgHY/0Gf8b78=");

            string appcast = System.IO.File.ReadAllText(@"D:\Demos\NetsparkleTest\output\appcast.xml");
            string signature = System.IO.File.ReadAllText(@"D:\Demos\NetsparkleTest\output\appcast.xml.signature");

            var appcastBytes = Encoding.UTF8.GetBytes(appcast);

            var result = signatureVerifier.VerifySignature(signature, appcastBytes);

result is Valid! Same thing downloaded from the server - no. This started happening with the multi-line changelog I guess (need to verify)

EDIT: yes, with the changelog removed it checks out remotely again. So there must be some multi-line mangling going on that wreaks havoc on the signature (like converting two byte crlf into cr, or otherwise)

EDIT 2: my publish location is https://github.com/icsharpcode/ILSpy/tree/gh-pages - that might be the source of the trouble. Windows crlf is converted (Windows default), and the target (job and gh pages itself) is Linux. So there might be the difference.

Again, don't have time at the moment, but writing this comment so I can remember later: SecurityMode.Strict checks for change log security hashes. If the change log ends up in the app cast file, perhaps we are looking for a non-existent change log signature that doesn't exist and isn't needed.

(Does SecurityMode.OnlyVerifySoftwareDownloads work?)

Appreciate your willingness to do back and forth, here. Sometimes the only way to grow is to get new eyes on it. I realize it's probably not fun to come and go "things are just broken!!" but I hope that through this we can make this work better for all. :)

EDIT: Re-read your comment. That would make a lot of sense, actually, if there are line ending changes happening. That would mess up the signature. Hrrrmmmmm....

Ticket to multi-line verification: https://github.com/icsharpcode/ILSpy/blob/gh-pages/.gitattributes

appcast.xml eol=crlf

However, it is very empty in my dialog compared to yours:

image

			_sparkle = new SparkleUpdater(
				"https://ilspy.net/appcast.xml",
				new Ed25519Checker(SecurityMode.Strict, "s2P6MPexSRSjod77aWUjgVKj/gKYYAeqgHY/0Gf8b78=")
			) {
				UIFactory = new NetSparkleUpdater.UI.WPF.UIFactory(Images.ILSpyIcon),
				RelaunchAfterUpdate = false,
				CustomInstallerArguments = "",
				Configuration = new JSONConfiguration(new NetSparkleUpdater.AssemblyAccessors.AssemblyReflectionAccessor(null), sparkleSettingsPath)
			};

I have integrated the accessor. And it is pointing to the entry assembly ILSpy.dll - which properties it is looking for to populate the dialog?

Is there some dark mode something going on there? I am guessing the text is there at the top, just invisible/white. I've seen that happen on the Avalonia build before. (Again, sorry, can't pop open a debugger at the moment..is this a windows 11 thing for WPF to go into dark mode?)

To answer your question more directly, https://github.com/NetSparkleUpdater/NetSparkle/blob/develop/src/NetSparkle/Interfaces/IAssemblyAccessor.cs. Basically, name of product, title, and version.

Yes, the theme was the culprit
image
I switched the theme yesterday to see if/how "off" the dialogs were - and forgot about that today.

So, yes, this is going to be the next issue on how to integrate styles because we don't really do strange things - I pushed my branch now to https://github.com/icsharpcode/ILSpy/tree/netsparkleupdater (MainWindow.xml.cs has the code I was talking about all the time)

Theme switching: View / Theme > ...
Manual update check: Help / Check for updates - I wedged NetSparkle in there in addition to the existing check to force the dialog for repetitive testing

Ok, great, glad that you figured out the app cast encoding issue.

RE: Theme switching — we probably need to file a new issue on this repo for that. This lib is not doing anything for styling, basically. Nothing special. I've run into this problem myself before and my "quick fix" was just to copy in the UI files and use my own UIFactory implementation (basically copied from the source code) so that the overall theme switching was picked up.

I honestly am not sure of the best way to solve the problem of "my app switches themes for everything and so should this library's UI code in this external DLL"; perhaps it will require some styling updates on the library's end or something. 🤔

Closing as the original issue has been resolved.

@christophwille What did you end up dokng to fix this?
I'm having the exact same issue and I tried changing the line endings to LF but whenever I generate the appcast with the .md notes, the package update fails on client side. If I don't include the md file the updates work fine. Same issue you are having but this is not being pushed to me git repo.
I too am on Windows.
I tried converting the crlf to lf in the .md file but when the appcast is created it has the crlf back in there so I converted them to lf but still update fails.

See the thread post about .gitattributes