chore: demo supports for automatic updating.

This commit is contained in:
NaBian 2024-06-02 22:34:10 +08:00
parent cfeef2d01a
commit 3d7595692c
8 changed files with 250 additions and 81 deletions

View File

@ -42,4 +42,13 @@ jobs:
tags: true
- name: Push to nuget
run: dotnet nuget push ./build/outputs/nuget/*nupkg -k ${{ secrets.NUGETTOKEN }}
run: dotnet nuget push ./build/outputs/nuget/HandyControl.*nupkg -k ${{ secrets.NUGETTOKEN }}
- name: Create github release
uses: softprops/action-gh-release@v2
with:
# For maximum compatibility, we choose the .net40
files: ./build/outputs/installer/net40/*
body_path: ./build/outputs/CHANGELOG.md
token: ${{ secrets.GITHUB_TOKEN }}
prerelease: ${{ inputs.pre_release }}

View File

@ -14,6 +14,7 @@ const string TagPrefix = "v";
const string RepositoryFolder = "..";
const string LibNuspecTemplateFilePath = "lib.nuspec.template";
const string LangNuspecTemplateFilePath = "lang.nuspec.template";
const string InstallerNuspecTemplateFilePath = "installer.nuspec.template";
const string ConfigFilePath = "build.config.xml";
var target = Argument("target", "build");
@ -25,25 +26,34 @@ var email = Argument("email", "836904362@qq.com");
var libVersion = "";
var nugetVersion = "";
var nugetFolder = "";
var installerFolder = "";
var year = $"{DateTime.Now.Year}";
var copyright = $"Copyright © HandyOrg {HandyControlBirthYear}-{year}";
var libNuspecFilePath = "";
var langNuspecFilePath = "";
var installerNuspecFilePath = "";
var squirrelExePath = "";
var gitRootPath = GitFindRootFromPath(MakeAbsolute(new DirectoryPath("."))).FullPath;
var propsFilePath = Combine(gitRootPath, "src/Directory.Build.Props");
var licenseFilePath = Combine(gitRootPath, "LICENSE");
var resxFileFolder = Combine(gitRootPath, "src/Shared/HandyControl_Shared/Properties/Langs");
var iconFilePath = Combine(gitRootPath, "src/Shared/HandyControlDemo_Shared/Resources/Img/icon.ico");
var buildConfig = new BuildConfig();
Setup(context =>
{
buildConfig = LoadBuildConfig();
nugetFolder = Combine(buildConfig.OutputsFolder, "nuget");
installerFolder = Combine(buildConfig.OutputsFolder, "installer");
libNuspecFilePath = Combine(nugetFolder, GetFileNameWithoutExtension(LibNuspecTemplateFilePath));
langNuspecFilePath = Combine(nugetFolder, LangNuspecTemplateFilePath);
installerNuspecFilePath = Combine(nugetFolder, InstallerNuspecTemplateFilePath);
CleanDirectory(buildConfig.OutputsFolder);
CreateDirectory(nugetFolder);
CreateDirectory(installerFolder);
context.Information($"preReleasePhase: {preReleasePhase}");
context.Information($"preRelease: {preRelease}");
@ -53,6 +63,9 @@ Setup(context =>
Copy(LibNuspecTemplateFilePath, libNuspecFilePath, true);
Copy(LangNuspecTemplateFilePath, langNuspecFilePath, true);
Copy(InstallerNuspecTemplateFilePath, installerNuspecFilePath, true);
DownloadSquirrelTools();
});
#region TASKS
@ -83,6 +96,7 @@ Task("update version")
{
ReplaceFileText(libNuspecFilePath, "version", nugetVersion);
ReplaceFileText(langNuspecFilePath, "version", nugetVersion);
ReplaceFileText(installerNuspecFilePath, "version", nugetVersion);
}
});
@ -95,6 +109,7 @@ Task("update copyright")
ReplaceFileText(libNuspecFilePath, "year", year);
ReplaceFileText(langNuspecFilePath, "year", year);
ReplaceFileText(installerNuspecFilePath, "year", year);
});
Task("commit files")
@ -110,6 +125,14 @@ Task("create tag")
GitTag(gitRootPath, $"{TagPrefix}{nugetVersion}");
});
Task("generate change log")
.Does(() =>
{
// TODO
WriteAllText(Combine(buildConfig.OutputsFolder, "CHANGELOG.md"), "");
});
Task("update nuget sha")
.Does(() =>
{
@ -117,6 +140,7 @@ Task("update nuget sha")
ReplaceFileText(libNuspecFilePath, "commit", lastCommit.Sha);
ReplaceFileText(langNuspecFilePath, "commit", lastCommit.Sha);
ReplaceFileText(installerNuspecFilePath, "commit", lastCommit.Sha);
});
Task("add nuget dependencies")
@ -146,28 +170,20 @@ Task("add nuget files")
foreach (BuildTask task in buildConfig.BuildTasks)
{
var frameworkFolder = $@"lib\{task.OutputsFolder}";
var libFolder = $@"lib\{task.OutputsFolder}";
if (!IsFramework(task.Framework))
{
AddFile(libDocument, $@"{frameworkFolder}\HandyControl.deps.json");
AddFile(libDocument, $@"{libFolder}\HandyControl.deps.json");
}
AddFile(libDocument, $@"{frameworkFolder}\HandyControl.dll");
AddFile(libDocument, $@"{frameworkFolder}\HandyControl.pdb");
AddFile(libDocument, $@"{frameworkFolder}\HandyControl.xml");
AddFile(langDocument, $@"{frameworkFolder}\{{lang}}\HandyControl.resources.dll");
AddFile(libDocument, $@"{libFolder}\HandyControl.dll");
AddFile(libDocument, $@"{libFolder}\HandyControl.pdb");
AddFile(libDocument, $@"{libFolder}\HandyControl.xml");
AddFile(langDocument, $@"{libFolder}\{{lang}}\HandyControl.resources.dll");
}
SaveXmlDocument(libDocument, libNuspecFilePath, indentation: 2);
SaveXmlDocument(langDocument, langNuspecFilePath, indentation: 2);
void AddFile(XmlDocument document, string filePath)
{
var fileItem = document.CreateElement("file");
fileItem.SetAttribute("src", $@"..\{filePath}");
fileItem.SetAttribute("target", filePath);
document.DocumentElement.SelectSingleNode("//files").AppendChild(fileItem);
}
});
Task("build lib")
@ -203,15 +219,20 @@ Task("build demo")
continue;
}
var dotNetBuildSettings = new DotNetBuildSettings
{
Configuration = task.Configuration,
Framework = task.Framework,
OutputDirectory = $"{buildConfig.OutputsFolder}/demo/{task.OutputsFolder}",
// remove pdb files
ArgumentCustomization = args => args
.Append("/p:DebugType=none")
};
DotNetBuild
(
$"{gitRootPath}/src/{task.Domain}/HandyControlDemo_{task.Domain}/HandyControlDemo_{task.Domain}.csproj",
new DotNetBuildSettings
{
Configuration = task.Configuration,
Framework = task.Framework,
OutputDirectory = $"{buildConfig.OutputsFolder}/demo/{task.OutputsFolder}",
}
dotNetBuildSettings
);
}
});
@ -228,15 +249,60 @@ Task("create lang nuspec files")
}
});
Task("create installer nuspec files")
.Does(() =>
{
string templateContent = ReadAllText(installerNuspecFilePath, Encoding.UTF8);
foreach (var task in buildConfig.BuildTasks)
{
string installerFilePath = Combine(nugetFolder, $"installer.{task.Framework.Replace(".", "")}.nuspec");
WriteAllText(installerFilePath, templateContent.Replace("{framework}", task.Framework.Replace(".", "")));
XmlDocument installerDocument = LoadXmlDocument(installerFilePath);
var demoFolder = $@"demo\{task.OutputsFolder}";
// Squirrel is expecting a single lib / net45 directory provided regardless of whether your app is a net45 application.
AddFile(installerDocument, $@"{demoFolder}\**\*", @"lib\net45");
SaveXmlDocument(installerDocument, installerFilePath, indentation: 2);
}
});
Task("pack nuspec files")
.Does(() =>
{
string nugetExePath = Context.Tools.Resolve("nuget.exe").FullPath;
StartProcess(nugetExePath, $"pack {nugetFolder}/lib.nuspec -Symbols -SymbolPackageFormat snupkg -OutputDirectory {nugetFolder}");
foreach (string nuspecFilePath in EnumerateFiles(nugetFolder, "lang.*.nuspec"))
foreach (string nuspecFilePath in GetAllLangNuspecFilePaths())
{
StartProcess(nugetExePath, $"pack {nuspecFilePath} -OutputDirectory {nugetFolder}");
}
foreach (string nuspecFilePath in GetAllInstallerNuspecFilePaths())
{
StartProcess(nugetExePath, $"pack {nuspecFilePath} -OutputDirectory {nugetFolder}");
}
});
Task("create demo installers")
.Does(() =>
{
if (!FileExists(squirrelExePath))
{
throw new Exception("Squirrel.exe not found.");
}
foreach (var task in buildConfig.BuildTasks)
{
Information(task.Framework);
string releaseDir = Combine(installerFolder, task.OutputsFolder);
CreateDirectory(releaseDir);
string installerFilePath = Combine(nugetFolder, $"HandyControlDemo-{task.Framework.Replace(".", "")}.{nugetVersion}.nupkg");
var cmd = $"--releasify {installerFilePath} --releaseDir {releaseDir} --setupIcon {iconFilePath} --icon {iconFilePath} --no-msi --no-delta";
StartProcess(squirrelExePath, cmd);
}
});
Task("publish")
@ -244,6 +310,7 @@ Task("publish")
.IsDependentOn("update version")
.IsDependentOn("update copyright")
.IsDependentOn("commit files")
.IsDependentOn("generate change log")
.IsDependentOn("create tag")
.IsDependentOn("update nuget sha")
.IsDependentOn("add nuget dependencies")
@ -251,11 +318,15 @@ Task("publish")
.IsDependentOn("build lib")
.IsDependentOn("build demo")
.IsDependentOn("create lang nuspec files")
.IsDependentOn("pack nuspec files");
.IsDependentOn("create installer nuspec files")
.IsDependentOn("pack nuspec files")
.IsDependentOn("create demo installers")
;
Task("build")
.IsDependentOn("build lib")
.IsDependentOn("build demo");
.IsDependentOn("build demo")
;
RunTarget(target);
@ -287,6 +358,14 @@ private void SaveXmlDocument(XmlDocument document, string xmlFilePath, int inden
}
}
public void AddFile(XmlDocument document, string filePath, string target = "")
{
var fileItem = document.CreateElement("file");
fileItem.SetAttribute("src", $@"..\{filePath}");
fileItem.SetAttribute("target", target == "" ? filePath : target);
document.DocumentElement.SelectSingleNode("//files").AppendChild(fileItem);
}
private void ReplaceFileText(string filePath, string key, string value)
{
WriteAllText(
@ -350,6 +429,10 @@ private IEnumerable<string> GetAllLangs()
}
}
private IEnumerable<string> GetAllInstallerNuspecFilePaths() => EnumerateFiles(nugetFolder, "installer.*.nuspec");
private IEnumerable<string> GetAllLangNuspecFilePaths() => EnumerateFiles(nugetFolder, "lang.*.nuspec");
private BuildConfig LoadBuildConfig()
{
var buildConfig = new BuildConfig();
@ -383,6 +466,18 @@ private BuildConfig LoadBuildConfig()
return buildConfig;
}
private void DownloadSquirrelTools()
{
const string url = "https://www.nuget.org/api/v2/package/squirrel.windows/2.0.1";
var nupkgFilePath = Combine("tools", "squirrel.windows.2.0.1.nupkg");
var toolFolderPath = Combine("tools", "squirrel.windows.2.0.1");
DownloadFile(url, nupkgFilePath);
Unzip(nupkgFilePath, toolFolderPath, true);
squirrelExePath = Combine(toolFolderPath, "tools", "Squirrel.exe");
}
#endregion
#region INNER TYPES

View File

@ -2,7 +2,7 @@
<outputsFolder>outputs</outputsFolder>
<tasks>
<task framework="net40" target=".NETFramework4.0" outputsFolder="net40" configuration="Release-Net40" domain="Net_40" buildLib="true" buildDemo="true"/>
<task framework="net45" target=".NETFramework4.5" outputsFolder="net45" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
<!-- <task framework="net45" target=".NETFramework4.5" outputsFolder="net45" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
<task framework="net451" target=".NETFramework4.5.1" outputsFolder="net451" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
<task framework="net452" target=".NETFramework4.5.2" outputsFolder="net452" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
<task framework="net46" target=".NETFramework4.6" outputsFolder="net46" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
@ -18,6 +18,6 @@
<task framework="net5.0-windows" target="net5.0" outputsFolder="net5.0" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
<task framework="net6.0-windows" target="net6.0" outputsFolder="net6.0" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
<task framework="net7.0-windows" target="net7.0" outputsFolder="net7.0" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
<task framework="net8.0-windows" target="net8.0" outputsFolder="net8.0" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/>
<task framework="net8.0-windows" target="net8.0" outputsFolder="net8.0" configuration="Release" domain="Net_GE45" buildLib="true" buildDemo="true"/> -->
</tasks>
</config>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>HandyControlDemo-{framework}</id>
<version>{version}</version>
<title>HandyControlDemo-{framework}</title>
<authors>HandyOrg</authors>
<owners>HandyOrg</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<license type="file">LICENSE</license>
<icon>icon.png</icon>
<projectUrl>https://github.com/HandyOrg/HandyControl</projectUrl>
<description>Contains some simple and commonly used WPF controls</description>
<releaseNotes>Changes are detailed at https://github.com/HandyOrg/HandyControl/releases</releaseNotes>
<copyright>Copyright © HandyOrg 2018-{year}</copyright>
<tags>WPF C# Control</tags>
<repository type="git" url="https://github.com/HandyOrg/HandyControl" commit="{commit}" />
<dependencies />
</metadata>
<files>
<file src="..\..\icon.png" target="icon.png" />
<file src="..\..\..\LICENSE" target="LICENSE" />
</files>
</package>

View File

@ -3,7 +3,7 @@
<metadata>
<id>HandyControl.Lang.{lang}</id>
<version>{version}</version>
<title>HandyOrg</title>
<title>HandyControl.Lang.{lang}</title>
<authors>HandyOrg</authors>
<owners>HandyOrg</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>

View File

@ -3,7 +3,7 @@
<metadata>
<id>HandyControl</id>
<version>{version}</version>
<title>HandyOrg</title>
<title>HandyControl</title>
<authors>HandyOrg</authors>
<owners>HandyOrg</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>

View File

@ -3,7 +3,7 @@
<metadata>
<id>HandyControl</id>
<version>{version}</version>
<title>HandyOrg</title>
<title>HandyControl</title>
<authors>HandyOrg</authors>
<owners>HandyOrg</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>

View File

@ -8,6 +8,7 @@ using System.Net;
using System.Runtime;
#endif
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using HandyControl.Data;
using HandyControl.Tools;
@ -26,63 +27,19 @@ public partial class App
public App()
{
#if !NET40
var cachePath = $"{AppDomain.CurrentDomain.BaseDirectory}Cache";
if (!Directory.Exists(cachePath))
{
Directory.CreateDirectory(cachePath);
}
ProfileOptimization.SetProfileRoot(cachePath);
ProfileOptimization.StartProfile("Profile");
#endif
EnsureProfileOptimization();
}
protected override void OnStartup(StartupEventArgs e)
{
AppMutex = new Mutex(true, "HandyControlDemo", out var createdNew);
EnsureSingleton();
OpenSplashScreen();
if (!createdNew)
{
var current = Process.GetCurrentProcess();
base.OnStartup(e);
foreach (var process in Process.GetProcessesByName(current.ProcessName))
{
if (process.Id != current.Id)
{
Win32Helper.SetForegroundWindow(process.MainWindowHandle);
break;
}
}
Shutdown();
}
else
{
var splashScreen = new SplashScreen("Resources/Img/Cover.png");
splashScreen.Show(true);
base.OnStartup(e);
UpdateRegistry();
ShutdownMode = ShutdownMode.OnMainWindowClose;
GlobalData.Init();
ConfigHelper.Instance.SetLang(GlobalData.Config.Lang);
LangProvider.Culture = new CultureInfo(GlobalData.Config.Lang);
if (GlobalData.Config.Skin != SkinType.Default)
{
UpdateSkin(GlobalData.Config.Skin);
}
ConfigHelper.Instance.SetWindowDefaultStyle();
ConfigHelper.Instance.SetNavigationWindowDefaultStyle();
#if NET40
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
#else
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
#endif
}
//UpdateRegistry();
ApplyConfiguration();
UpdateApp();
}
protected override void OnExit(ExitEventArgs e)
@ -112,7 +69,71 @@ public partial class App
Current.MainWindow?.OnApplyTemplate();
}
private void UpdateRegistry()
private void ApplyConfiguration()
{
ShutdownMode = ShutdownMode.OnMainWindowClose;
GlobalData.Init();
ConfigHelper.Instance.SetLang(GlobalData.Config.Lang);
LangProvider.Culture = new CultureInfo(GlobalData.Config.Lang);
if (GlobalData.Config.Skin != SkinType.Default)
{
UpdateSkin(GlobalData.Config.Skin);
}
ConfigHelper.Instance.SetWindowDefaultStyle();
ConfigHelper.Instance.SetNavigationWindowDefaultStyle();
#if NET40
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
#else
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
#endif
}
private static void OpenSplashScreen()
{
var splashScreen = new SplashScreen("Resources/Img/Cover.png");
splashScreen.Show(true);
}
private static void EnsureProfileOptimization()
{
#if !NET40
var cachePath = $"{AppDomain.CurrentDomain.BaseDirectory}Cache";
if (!Directory.Exists(cachePath))
{
Directory.CreateDirectory(cachePath);
}
ProfileOptimization.SetProfileRoot(cachePath);
ProfileOptimization.StartProfile("Profile");
#endif
}
private void EnsureSingleton()
{
AppMutex = new Mutex(true, "HandyControlDemo", out var createdNew);
if (createdNew)
{
return;
}
var current = Process.GetCurrentProcess();
foreach (var process in Process.GetProcessesByName(current.ProcessName))
{
if (process.Id != current.Id)
{
Win32Helper.SetForegroundWindow(process.MainWindowHandle);
break;
}
}
Shutdown();
}
private static void UpdateRegistry()
{
var processModule = Process.GetCurrentProcess().MainModule;
if (processModule != null)
@ -136,4 +157,24 @@ public partial class App
}
}
}
private static void UpdateApp()
{
const string api = "https://github.com/handyorg/handycontrol/releases/latest";
try
{
var mainDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.TrimEnd('\\'));
var updateExePath = Path.Combine(mainDirectory, "Update.exe");
if (File.Exists(updateExePath))
{
Task.Factory.StartNew(() => Process.Start(updateExePath, $"--update={api}"));
}
}
catch
{
// ignored
}
}
}