diff --git a/.github/workflows/debug.yml.disabled b/.github/workflows/debug.yml.disabled new file mode 100644 index 00000000..4db1947b --- /dev/null +++ b/.github/workflows/debug.yml.disabled @@ -0,0 +1,13 @@ +name: CI +on: [push] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..7c978b31 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,59 @@ +name: deploy +on: + push: + branches: [dev, main, master] + pull_request: + branches: [dev, main, master] + +env: + version: 1.1.0.0 + +jobs: + deploy-to-tencent-cos: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Setup .NET Core + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Setup dotnet-script + run: dotnet tool install --global dotnet-script + - name: Pre-Process + run: dotnet script .github/preProcess/MauiEnvConfig.csx + - name: Install Workloads + run: dotnet workload install maui-windows + - name: Create Folders need + run: | + mkdir D:\a\installer + mkdir D:\a\publish + - name: Copy THUAI7 + run: Copy-Item -recurse D:\a\THUAI7\THUAI7\ D:\a\mirror\ + - name: Test + run: tree D:\a\mirror + - name: Remove directories not needed + run: | + Remove-Item -recurse -force D:\a\mirror\.git + Remove-Item -recurse D:\a\mirror\.github + Remove-Item -recurse D:\a\mirror\installer + Remove-Item -recurse D:\a\mirror\interface + Remove-Item -recurse D:\a\mirror\logic + - name: Build Server + run: | + mkdir D:\a\mirror\logic + dotnet build "./logic/Server/Server.csproj" -o "D:\a\mirror\logic\Server" -p:WindowsAppSDKSelfContained=true -c Release + - name: Build Client + run: dotnet publish "./logic/Client/Client.csproj" -o "D:\a\mirror\logic\Client" -f net8.0-windows10.0.19041.0 -c Release -p:RuntimeIdentifierOverride=win10-x64 -p:WindowsPackageType=None -p:WindowsAppSDKSelfContained=true + - name: Deploy to bucket + run: dotnet run --project "./dependency/deploy/deploy.csproj" ${{ secrets.INSTALLER_COS_SECRET_ID }} ${{ secrets.INSTALLER_COS_SECRET_KEY }} + - name: Get installer package(No Key contained for safety) + run: | + $version=Get-ChildItem -Path D:\a\publish | ForEach-Object { $_.name } + [Environment]::SetEnvironmentVariable("version", $version, "Machine") + dotnet publish "./installer/installer.csproj" -o "D:\a\installer" -f net8.0-windows10.0.19041.0 -c Release -p:RuntimeIdentifierOverride=win10-x64 -p:WindowsPackageType=None -p:WindowsAppSDKSelfContained=true + ./dependency/7z/7za.exe a -r D:\a\publish\Installer_v${version}.zip D:\a\installer\* + - name: Upload installer package + uses: actions/upload-artifact@v4 + with: + name: Installer_v${{ env.version }}.zip + path: D:\a\publish\Installer_v${{ env.version }}.zip diff --git a/dependency/7z/7za.dll b/dependency/7z/7za.dll new file mode 100644 index 00000000..b19a1304 Binary files /dev/null and b/dependency/7z/7za.dll differ diff --git a/dependency/7z/7za.exe b/dependency/7z/7za.exe new file mode 100644 index 00000000..383d8e30 Binary files /dev/null and b/dependency/7z/7za.exe differ diff --git a/dependency/deploy/BaseViewModel.cs b/dependency/deploy/BaseViewModel.cs new file mode 100644 index 00000000..d8d0256f --- /dev/null +++ b/dependency/deploy/BaseViewModel.cs @@ -0,0 +1,117 @@ +using System; +using System.ComponentModel; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Globalization; +using System.Windows.Input; +using System.Runtime.CompilerServices; + +namespace installer.ViewModel +{ + public abstract class NotificationObject : INotifyPropertyChanged + { + public event PropertyChangedEventHandler? PropertyChanged; + /// + ///announce notification + /// + ///property name + public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } + /// + ///BaseCommand + /// + public class BaseCommand : ICommand + { + private Func? _canExecute; + private Action _execute; + + public BaseCommand(Func? canExecute, Action execute) + { + _canExecute = canExecute; + _execute = execute; + } + + public BaseCommand(Action execute) : + this(null, execute) + { + } + + + public event EventHandler? CanExecuteChanged + { + add + { + if (_canExecute != null) + { + //CommandManager.RequerySuggested += value; + } + } + remove + { + if (_canExecute != null) + { + //CommandManager.RequerySuggested -= value; + } + } + } + + public bool CanExecute(object? parameter) + { + return _canExecute == null ? true : _canExecute(parameter); + } + + public void Execute(object? parameter) + { + if (_execute != null && CanExecute(parameter)) + { + _execute(parameter); + } + } + } + + public class RadioConverter + { + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value == null || parameter == null) + { + return false; + } + string checkvalue = value.ToString() ?? ""; + string targetvalue = parameter.ToString() ?? ""; + bool r = checkvalue.Equals(targetvalue); + return r; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is null || parameter is null) + { + return null; + } + + if ((bool)value) + { + return parameter.ToString(); + } + return null; + } + } + + public abstract class BaseViewModel : NotificationObject + { + private const string constBackgroundColor = "White"; + public string ConstBackgroundColor { get => constBackgroundColor; } + + private const string constFontSize = "18"; + public string ConstFontSize { get => constFontSize; } + + private const string constTextColor = "Blue"; + public string ConstTextColor { get => constTextColor; } + } +} diff --git a/dependency/deploy/Data/FakeData.cs b/dependency/deploy/Data/FakeData.cs new file mode 100644 index 00000000..e9070a6f --- /dev/null +++ b/dependency/deploy/Data/FakeData.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace installer.Data +{ + public enum DevicePlatform + { + WinUI + } + public static class DeviceInfo + { + public static DevicePlatform Platform { get; set; } = DevicePlatform.WinUI; + } +} diff --git a/dependency/deploy/MauiProgram.cs b/dependency/deploy/MauiProgram.cs new file mode 100644 index 00000000..ba2399d2 --- /dev/null +++ b/dependency/deploy/MauiProgram.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace installer +{ + public static class MauiProgram + { + public static bool RefreshLogs_WhileDebug = false; + public static bool ErrorTrigger_WhileDebug = true; + public static string SecretID = "***"; + public static string SecretKey = "***"; + } +} diff --git a/dependency/deploy/Program.cs b/dependency/deploy/Program.cs new file mode 100644 index 00000000..7531360e --- /dev/null +++ b/dependency/deploy/Program.cs @@ -0,0 +1,72 @@ +using installer.Data; +using installer.Model; +using installer.Services; +using System.Collections.Concurrent; +using System.Diagnostics; + +Logger Log = LoggerProvider.FromConsole(); + +// 全权读写 +Tencent_Cos Cloud = new Tencent_Cos("1319625962", "ap-beijing", "thuai7", Log); +Cloud.UpdateSecret(args[0], args[1]); + +Downloader d = new Downloader(); +d.Cloud.UpdateSecret(args[0], args[1]); +d.Data.Config.InstallPath = @"D:\a\mirror\"; + +d.Log.Partner.Add(Log); +// 每次更新需要更新默认值 +d.CurrentVersion = new TVersion(); +File.Create(Path.Combine("D:\\a\\publish", d.CurrentVersion.InstallerVersion.ToString())); + +if (d.CheckUpdate()) +{ + foreach (var r in d.Data.MD5Update) + { + Log.LogInfo($"{r.state}, {r.name}"); + } + + d.Data.SaveMD5Data(); + List l = new List(); + foreach (var r in d.Data.MD5Update) + { + var n = r.name.Replace('\\', '/'); + n = n.TrimStart('.').TrimStart('/'); + if (r.state == System.Data.DataRowState.Added || r.state == System.Data.DataRowState.Modified) + { + l.Add(Cloud.UploadFileAsync(Path.Combine(d.Data.Config.InstallPath, r.name), n)); + } + else if (r.state == System.Data.DataRowState.Deleted) + { + l.Add(Cloud.DeleteFileAsync(n)); + } + } + Task.WaitAll(l.ToArray()); +} +else +{ + Log.LogInfo("Nothing to update"); +} + +d.Data.SaveMD5Data(); +Cloud.UploadFile(d.Data.MD5DataPath, "hash.json"); + +Cloud.UploadFile(Path.Combine(d.Data.Config.InstallPath, "CAPI", "cpp", "API", "src", "AI.cpp"), + $"Templates/t.{d.CurrentVersion.TemplateVersion}.cpp"); +Cloud.UploadFile(Path.Combine(d.Data.Config.InstallPath, "CAPI", "python", "PyAPI", "AI.py"), + $"Templates/t.{d.CurrentVersion.TemplateVersion}.py"); +Log.LogInfo("User code uploaded."); + +var list = (from i in d.Data.MD5Data + select i.Key.Replace(Path.DirectorySeparatorChar, '/').TrimStart('.').TrimStart('/')).ToArray(); +Log.LogInfo(list[0]); +using (FileStream s = new FileStream(Path.Combine(d.Data.Config.InstallPath, "compress.csv"), FileMode.Create, FileAccess.Write)) +using (StreamWriter w = new StreamWriter(s)) +{ + foreach (var item in list) + { + w.WriteLine("https://thuai7-1319625962.cos.ap-beijing.myqcloud.com/" + item); + } +} +Cloud.UploadFile(Path.Combine(d.Data.Config.InstallPath, "compress.csv"), "compress.csv"); +Log.LogInfo("Compress csv generated."); diff --git a/dependency/deploy/Services/OtherService.cs b/dependency/deploy/Services/OtherService.cs new file mode 100644 index 00000000..94e080d5 --- /dev/null +++ b/dependency/deploy/Services/OtherService.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace installer.Services +{ + public static class Application + { + public static App? Current; + } + + public class App + { + public void Quit() + { + + } + } +} diff --git a/dependency/deploy/deploy.csproj b/dependency/deploy/deploy.csproj new file mode 100644 index 00000000..6dbb2996 --- /dev/null +++ b/dependency/deploy/deploy.csproj @@ -0,0 +1,31 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dependency/deploy/deploy.sln b/dependency/deploy/deploy.sln new file mode 100644 index 00000000..d40fc769 --- /dev/null +++ b/dependency/deploy/deploy.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34309.116 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deploy", "Deploy.csproj", "{1E0F079D-EECF-4DC8-A783-A86F25A2397E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1E0F079D-EECF-4DC8-A783-A86F25A2397E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E0F079D-EECF-4DC8-A783-A86F25A2397E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E0F079D-EECF-4DC8-A783-A86F25A2397E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E0F079D-EECF-4DC8-A783-A86F25A2397E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7B9F1497-D14B-47E1-AEEF-A4254398185F} + EndGlobalSection +EndGlobal diff --git a/installer/Data/MD5FileData.cs b/installer/Data/MD5FileData.cs index ef3ba870..65a63cda 100644 --- a/installer/Data/MD5FileData.cs +++ b/installer/Data/MD5FileData.cs @@ -26,13 +26,13 @@ public class TVersion { // 代码库版本 [JsonInclude] - public Version LibVersion = new Version(1, 0, 2, 2); + public Version LibVersion = new Version(1, 0, 2, 3); // 选手代码模板版本 [JsonInclude] public Version TemplateVersion = new Version(1, 0, 0, 3); // 本体版本 [JsonInclude] - public Version InstallerVersion = new Version(1, 0, 2, 0); + public Version InstallerVersion = new Version(1, 1, 0, 0); public static bool operator <(TVersion l, TVersion r) { return l.LibVersion < r.LibVersion || l.TemplateVersion < r.TemplateVersion || l.InstallerVersion < r.InstallerVersion; diff --git a/installer/MauiProgram.cs b/installer/MauiProgram.cs index 3040057e..2bf8f9ef 100755 --- a/installer/MauiProgram.cs +++ b/installer/MauiProgram.cs @@ -67,7 +67,7 @@ public static MauiApp CreateMauiApp() var c = builder.Services.AddSingleton().First(); builder.Services.AddSingleton(FolderPicker.Default); - + builder.Services.AddSingleton(FilePicker.Default); AddViewModelService(builder); AddPageService(builder); diff --git a/installer/Model/Downloader.cs b/installer/Model/Downloader.cs index 6b1981a6..e444d33e 100755 --- a/installer/Model/Downloader.cs +++ b/installer/Model/Downloader.cs @@ -190,28 +190,42 @@ public void Install(string? path = null) if (Log is FileLogger) ((FileLogger)Log).Path = Path.Combine(Data.Config.InstallPath, "Logs", "Installer.log"); Data.ResetInstallPath(Data.Config.InstallPath); - string zp = Path.Combine(Data.Config.InstallPath, "THUAI7.tar.gz"); Status = UpdateStatus.downloading; (CloudReport.ComCount, CloudReport.Count) = (0, 1); - Cloud.Log.LogInfo($"正在下载安装包……"); + Cloud.Log.LogInfo($"正在下载installer安装包……"); Cloud.DownloadFileAsync(zp, "THUAI7.tar.gz").Wait(); CloudReport.ComCount = 1; Status = UpdateStatus.unarchieving; - Cloud.Log.LogInfo($"安装包下载完毕,正在解压……"); + Cloud.Log.LogInfo($"installer安装包下载完毕,正在解压……"); Cloud.ArchieveUnzip(zp, Data.Config.InstallPath); - Cloud.Log.LogInfo($"解压完成"); + Cloud.Log.LogInfo($"installer解压完成"); File.Delete(zp); CurrentVersion = Data.FileHashData.TVersion; Cloud.Log.LogInfo("正在下载选手代码……"); Status = UpdateStatus.downloading; - CloudReport.Count = 3; + CloudReport.Count += 2; var tocpp = Cloud.DownloadFileAsync(Path.Combine(Data.Config.InstallPath, "CAPI", "cpp", "API", "src", "AI.cpp"), $"./Templates/t.{CurrentVersion.TemplateVersion}.cpp").ContinueWith(_ => CloudReport.ComCount++); var topy = Cloud.DownloadFileAsync(Path.Combine(Data.Config.InstallPath, "CAPI", "python", "PyAPI", "AI.py"), $"./Templates/t.{CurrentVersion.TemplateVersion}.py").ContinueWith(_ => CloudReport.ComCount++); Task.WaitAll(tocpp, topy); + + Cloud.Report.Count += 1; + zp = Path.Combine(Data.Config.InstallPath, "protoCpp.tar.gz"); + Cloud.Log.LogInfo("正在下载proto cpp库……"); + Cloud.DownloadFileAsync(zp, "Setup/proto/protoCpp.tar.gz").Wait(); + CloudReport.ComCount += 1; + Status = UpdateStatus.unarchieving; + Cloud.Log.LogInfo($"proto cpp库下载完毕,正在解压……"); + var protoCppLibPath = Path.Combine(Data.Config.InstallPath, "CAPI", "cpp", "lib"); + if (!Directory.Exists(protoCppLibPath)) + Directory.CreateDirectory(protoCppLibPath); + Cloud.ArchieveUnzip(zp, protoCppLibPath); + Cloud.Log.LogInfo($"proto cpp库解压完成"); + File.Delete(zp); + if (CloudReport.ComCount == CloudReport.Count) { Cloud.Log.LogInfo("选手代码下载成功!"); @@ -296,7 +310,7 @@ public bool CheckUpdate(bool writeMD5 = true) Status = UpdateStatus.success; if (Data.MD5Update.Count != 0 || CurrentVersion < Data.FileHashData.TVersion) { - Data.Log.LogInfo("需要更新,请点击更新按钮以更新。"); + Data.Log.LogInfo("代码库需要更新,请点击更新按钮以更新。"); if (writeMD5) { Data.SaveMD5Data(); @@ -312,6 +326,15 @@ public bool CheckUpdate(bool writeMD5 = true) } return true; } + else if (!Directory.Exists(Path.Combine(Data.Config.InstallPath, "CAPI", "cpp", "lib"))) + { + Data.Log.LogInfo("未检测到proto cpp库,请点击更新按钮以修复。"); + if (writeMD5) + { + Data.SaveMD5Data(); + } + return true; + } else { Data.Log.LogInfo("您的版本已经是最新版本!"); @@ -370,7 +393,23 @@ public int Update() return -1; } } - + // 如果缺少proto cpp库,应当立刻下载 + if (!Directory.Exists(Path.Combine(Data.Config.InstallPath, "CAPI", "cpp", "lib"))) + { + Cloud.Report.Count += 1; + string zp = Path.Combine(Data.Config.InstallPath, "protoCpp.tar.gz"); + Cloud.Log.LogInfo("正在下载proto cpp库……"); + Cloud.DownloadFileAsync(zp, "Setup/proto/protoCpp.tar.gz").Wait(); + CloudReport.ComCount += 1; + Status = UpdateStatus.unarchieving; + Cloud.Log.LogInfo($"proto cpp库下载完毕,正在解压……"); + var protoCppLibPath = Path.Combine(Data.Config.InstallPath, "CAPI", "cpp", "lib"); + if (!Directory.Exists(protoCppLibPath)) + Directory.CreateDirectory(protoCppLibPath); + Cloud.ArchieveUnzip(zp, protoCppLibPath); + Cloud.Log.LogInfo($"proto cpp库解压完成"); + File.Delete(zp); + } // 启动器本身需要更新,返回结果为16 if (CurrentVersion.InstallerVersion < Data.FileHashData.TVersion.InstallerVersion) { diff --git a/installer/Model/Local_Data.cs b/installer/Model/Local_Data.cs index 27cbed03..c2cb39ae 100755 --- a/installer/Model/Local_Data.cs +++ b/installer/Model/Local_Data.cs @@ -216,6 +216,7 @@ public void SaveMD5Data(bool VersionRefresh = true) })); sw.Flush(); } + // Log.LogInfo("MD5Data saved."); } catch (Exception e) { @@ -288,8 +289,8 @@ public void ScanDir(bool VersionRefresh = true) public static bool IsUserFile(string filename) { filename = filename.Replace(Path.DirectorySeparatorChar, '/'); - if (filename.Contains("/git/") || filename.Contains("bin/") || filename.Contains("/obj/") || filename.Contains("/x64/") - || filename.Contains("__pycache__")) + if (filename.Contains("/git/") || filename.Contains("/bin/") || filename.Contains("/obj/") || filename.Contains("/x64/") + || filename.Contains("__pycache__") || filename.Contains("/CAPI/cpp/lib/")) return true; if (filename.Contains("/vs/") || filename.Contains("/.vs/") || filename.Contains("/.vscode/")) return true; diff --git a/installer/Model/Tencent_Cos.cs b/installer/Model/Tencent_Cos.cs index 0bc0998f..b717b704 100755 --- a/installer/Model/Tencent_Cos.cs +++ b/installer/Model/Tencent_Cos.cs @@ -10,6 +10,8 @@ using System; using installer.Data; using System.Threading.Tasks; +using COSXML.Model.Bucket; +using COSXML.Model.Tag; // 禁用对没有调用异步API的异步函数的警告 #pragma warning disable CS1998 @@ -153,12 +155,14 @@ public int DownloadQueue(string basePath, IEnumerable queue) int thID = Log.StartNew(); Log.LogDebug(thID, "Batch download task started."); var array = queue.ToArray(); - Report.Count = array.Count(); - Report.ComCount = 0; - if (Report.Count == 0) + var count = array.Length; + if (count == 0) return 0; - var partitionar = Partitioner.Create(0, Report.Count, Report.Count / 4 > 0 ? Report.Count / 4 : Report.Count); - var c = 0; + var comCount = 0; + var comCountOld = Report.ComCount; + Report.Count += count; + + var partitionar = Partitioner.Create(0, count, count / 4 > 0 ? count / 4 : count); Parallel.ForEach(partitionar, (range, loopState) => { for (long i = range.Item1; i < range.Item2; i++) @@ -177,14 +181,14 @@ public int DownloadQueue(string basePath, IEnumerable queue) } finally { - Interlocked.Increment(ref c); - Report.ComCount = c; + Interlocked.Increment(ref comCount); + Report.ComCount = comCount + comCountOld; Log.LogInfo(thID, $"Child process: {subID} finished."); } } }); Log.LogInfo(thID, "Batch download task finished."); - Report.ComCount = Report.Count; + Report.ComCount = comCount + comCountOld; return thID; } @@ -300,6 +304,37 @@ public void DeleteFile(string remotePath) } } + public List EnumerateDir(string remotePath) + { + int thID = Log.StartNew(); + var result = new List(); + string bucket = $"{BucketName}-{Appid}"; + remotePath = remotePath.TrimStart('.').TrimStart('/'); + Log.LogInfo(thID, $"Enumerate files in {remotePath}"); + + bool truncated = false; + string marker = string.Empty; + do + { + GetBucketRequest request = new GetBucketRequest(bucket); + request.SetPrefix(remotePath); + request.SetDelimiter("/"); + if (!string.IsNullOrEmpty(marker)) + request.SetMarker(marker); + //执行请求 + GetBucketResult res = cosXml.GetBucket(request); + ListBucket info = res.listBucket; + result.AddRange(info.contentsList.Select(i => i.key).Where(i => i != remotePath)); + foreach (var dir in info.commonPrefixesList) + { + result.AddRange(EnumerateDir(dir.prefix)); + } + truncated = info.isTruncated; + marker = info.nextMarker; + } while (truncated); + + return result; + } #region 异步方法包装 public Task DownloadFileAsync(string savePath, string? remotePath = null) { diff --git a/installer/Page/DebugPage.xaml b/installer/Page/DebugPage.xaml index 760eb20d..da6acc56 100644 --- a/installer/Page/DebugPage.xaml +++ b/installer/Page/DebugPage.xaml @@ -3,207 +3,208 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="installer.Page.DebugPage" Title="Launcher"> - - + - - - - - Client + Server + + + + + + - - - -