C
C#•3y ago
ero

Need help creating a small installer

Using Spectre.Console, I wanna create a really small console app which simply downloads Trace Spy (https://github.com/smourier/TraceSpy), a custom config for it, and a font to go with it (Cascadia Code). I just need some guidance on how to use Spectre, and some insights into best practices and the like. I'll also need a lot of help getting progress bars to work, as well as unzipping and installing the font for the user.
76 Replies
ero
eroOP•3y ago
let me start with the obvious bit; letting the user choose the directory which they would like to download WpfTraceSpy.exe to. my current choice is the user's desktop. should i change this to the directory they opened the installer in?
AnsiConsole.Write(new Rule("[yellow]Directory[/]").LeftAligned());

string desktopPath = Path.Join(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Desktop");

string directory = AnsiConsole.Prompt<string>(
new TextPrompt<string>("Where would you like to install Trace Spy?")
.DefaultValue(desktopPath)
.ValidationErrorMessage("Path is not a valid directory or directory cannot be accessed.")
.Validate(path =>
{
try
{
using FileStream tempStream = File.Create(
Path.Combine(path, Path.GetRandomFileName()),
1,
FileOptions.DeleteOnClose);

return true;
}
catch
{
return false;
}
}));
AnsiConsole.Write(new Rule("[yellow]Directory[/]").LeftAligned());

string desktopPath = Path.Join(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Desktop");

string directory = AnsiConsole.Prompt<string>(
new TextPrompt<string>("Where would you like to install Trace Spy?")
.DefaultValue(desktopPath)
.ValidationErrorMessage("Path is not a valid directory or directory cannot be accessed.")
.Validate(path =>
{
try
{
using FileStream tempStream = File.Create(
Path.Combine(path, Path.GetRandomFileName()),
1,
FileOptions.DeleteOnClose);

return true;
}
catch
{
return false;
}
}));
my reasoning for the Validate func is that a try-catch there will eliminate absolutely any and all doubt that this is a directory i can save the .exe in that should be fine, yes?
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
it's a singular exe, i don't think i want to hide that in appdata where someone may not find it after installation also it's not my app honestly i'm not too happy with spectre as a whole
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
like sure, you can set a "default value" for a prompt, but for an inexperienced user, this might not look like a "default" they can just hit return on
ero
eroOP•3y ago
you can't insert the default where the user is typing, you can't tab-complete paths when typing very underwhelming
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
yeah i think i'ma just drop this whole thing lol too much work for what it's worth
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
basically, yeah my audience would be using it to read ODS traces
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
ehhh i dunno i need to download the tracespy exe, which i need to get from the latest release on the github repo i need to get cascadia code, also from the latest release on github, unzip, and install all font faces and i need to download a config for tracespy, but that's really not an issue, literally just a plain text file
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
i mean, i can bundle all that into an app too the point is to download and extract it dynamically in case there's a new release
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
i mean i could trim it, right $singlefile
MODiX
MODiX•3y ago
dotnet publish -c Release -r <runtime identifier> -p:PublishSingleFile=true Use of -r|--runtime implies --self-contained true. Add --self-contained false to publish as runtime-dependent. -r ... and -p:PublishSingleFile=true can be moved to .csproj as the following properties:
<RuntimeIdentifier>runtime identifier</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
<RuntimeIdentifier>runtime identifier</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
but to target multiple RIDs, you have to use dotnet publish with the -r option for each RID. You can also add -p:IncludeNativeLibrariesForSelfExtract=true to include native libraries (like Common Language Runtime dlls) in the output executable. https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file https://docs.microsoft.com/en-us/dotnet/core/rid-catalog https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish
Create a single file for application deployment - .NET
Learn what single file application is and why you should consider using this application deployment model.
.NET Runtime Identifier (RID) catalog
Learn about the Runtime Identifier (RID) and how RIDs are used in .NET.
ero
eroOP•3y ago
meh, whichever it is
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
but yeah i don't even know how to get a file from the latest release on github cause github.com/Foo/Bar/release/latest redirects to the actual tag okay, i may have a way. but i need some web api help, since i don't understand much of it github has an api
ero
eroOP•3y ago
for example, https://api.github.com/repos/smourier/TraceSpy/releases/latest will get me the latest release all downloadable files are stored in the assets array it would seem and the actual download url of the asset is under browser_download_url i'm not sure if it would be bad practice to just hard code that url?
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
pip
pip•3y ago
i mean it depends on the nature of your app, and your future plans. if you are simply using this application as a one-off and don't care about reusability and extendability, then i see no problems with hard-coding that url. if you plan to extend/retune this app later then going directly through github api sounds better. if the url changes in the future, the github api should catch that and your program would automatically adjust.
Anton
Anton•3y ago
if it needs to be user friendly, you might be better off making a dialog window app instead
ero
eroOP•3y ago
it has like 2 prompts buuut you're not entirely wrong with a gui you can at least browse your pc with the explorer to choose your install directory
Anton
Anton•3y ago
yeah
ero
eroOP•3y ago
and while you can probably also do that in a console app, it's much less expected
Anton
Anton•3y ago
a neat gui would probably be more work too I know a game where the dev built their updater in spectre console, IRule maybe it's open source, idk
ero
eroOP•3y ago
don't really care about the work i know wpf pretty well a neat gui wouldn't take me more than a few minutes
Anton
Anton•3y ago
wpf is only for Windows tho, keep that in mind
ero
eroOP•3y ago
so is trace spy
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
i do :) as in, i literally do need help already. i'm not sure how to get started
pip
pip•3y ago
like interacting with a REST API?
ero
eroOP•3y ago
ya never done it
pip
pip•3y ago
now is as good a time as any, REST APIs are so fun
ero
eroOP•3y ago
not very familiar with anything web related, really
pip
pip•3y ago
have you gone through tutorials like this yet? https://zetcode.com/csharp/httpclient/
C# HttpClient - creating HTTP requests with HttpClient in C#
C# HttpClient tutorial shows how to create HTTP requests with HttpClient in C#. In the examples, we create simple GET, HEAD, and POST requests.
ero
eroOP•3y ago
never is there not a more... official one?
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
pip
pip•3y ago
HttpClient Class (System.Net.Http)
Provides a class for sending HTTP requests and receiving HTTP responses from a resource identified by a URI.
pip
pip•3y ago
Web Dev Simplified
YouTube
What is REST?
If you have used any modern website, then chances are you have interacted with a website that uses REST. Even YouTube uses RESTful URLs on its site, but what exactly is REST? For starters, REST stands for Representation State Transfer and is just a fancy way of saying that a server responds to create, read, update, and delete requests in a stand...
pip
pip•3y ago
if you want to use an easier framework to start, try looking up RestSharp Although i think the general consensus is httpclient is better because no dependencies
ero
eroOP•3y ago
just skimming over the very first link, does it at all go over those like, parameters or whatever you'd call them? /repos/{owner}/{repo}/releases/{release_id}
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
cause as of now i only see those as placeholders that i would still just hard code or, if anything, pass them as args into a method
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
and then just $"api.github.com/repos/{owner}/{repo}/releases/{release_id}" man i hate this stuff, it's so complicated
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
pip
pip•3y ago
i can guarantee though that while it's complicated now, it'll be so easy when you get the hang of it. and it's super fun. you can literally make anything interact with anything, automations, bots quite frankly, making the installer itself sounds harder than REST to me 😄
ero
eroOP•3y ago
other way around for me well, frankly, i could just hard code all urls but that doesn't feel right
pip
pip•3y ago
here's your plan if you want to do it via the API and don't want to hard code it -> use the get request at the Git API endpoint you linked before -> use https://json2csharp.com/ to get its models(you can play with the casing settings at the bottom) -> Use your JsonSerializer to convert the GET request's response to an object -> get the urls from the object's
browser_download_url
browser_download_url
field -> you're happy and fulfilled now
Convert JSON to C# Classes Online - Json2CSharp Toolkit
Convert any JSON object to C# classes online. Json2CSharp is a free toolkit that will help you generate C# classes on the fly.
ero
eroOP•3y ago
the only difficult part here is step 1 i know how to work with json just fine
pip
pip•3y ago
so the first part is probably the easiest, from the docs above you just do
static readonly HttpClient client = new HttpClient();
async Task YourFunction()
{
HttpResponseMessage httpResponse = await client.GetAsync("http://www.contoso.com/");
string stringResponse = response.Content.ReadAsStringAsync();
//do your jsonserialization with the string response
}
static readonly HttpClient client = new HttpClient();
async Task YourFunction()
{
HttpResponseMessage httpResponse = await client.GetAsync("http://www.contoso.com/");
string stringResponse = response.Content.ReadAsStringAsync();
//do your jsonserialization with the string response
}
ero
eroOP•3y ago
so, what, i still just hard code the url?
pip
pip•3y ago
for the api endpoint? ye probably. if the endpoint has specific vals that you want the user to be able to change, pass those in like the example you had above
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
pip
pip•3y ago
yup good point, you can have the baseurl read from a config or server or anything
ero
eroOP•3y ago
hm. i mean i've used httpclient like that before, i thought there might be like... some other magic behind it
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
hm. i mean at that point almost no difficulty remains really the hardest part is unzipping and installing the fonts
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
ero
eroOP•3y ago
shouldn't that be using HttpClient client = new HttpClient(); :p
pip
pip•3y ago
NOPE!
ero
eroOP•3y ago
HUH
pip
pip•3y ago
oops didn't mean for caps there hahaa
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
pip
pip•3y ago
i'm pretty sure the HttpClient should be static/singleton or else you're going to be exhausting connection pool
ero
eroOP•3y ago
i can do using because i'm just gonna download all the things in the same method call
pip
pip•3y ago
well yea I guess my point here is that httpclient shouldn't be a local variable, it should be defined statically at the class level or managed via a factory
ero
eroOP•3y ago
ya
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
pip
pip•3y ago
so i guess technically you're right, if it's local then it should have using
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
pip
pip•3y ago
yea i don't think it's that huge of a deal, but i remember someone yelling at me that "using" is bad bad bad on httpclient, so it just stuck with me haha
Want results from more Discord servers?
Add your server