From f47975af0630bfdfd95258424a3f01eba90fcb75 Mon Sep 17 00:00:00 2001 From: zerlei <1445089819@qq.com> Date: Sat, 12 Oct 2024 17:25:18 +0800 Subject: [PATCH] =?UTF-8?q?fix=20&=20feat:=20=E6=94=B9=E4=BA=86=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E5=8F=91=E5=B8=83=E7=9A=84bug,=E5=8D=95?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=89=8D=E7=AB=AF=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - local server 前端页面 --- Server/Common/FileDirOp.cs | 2 +- Server/RemoteServer/StateHelper.cs | 78 +++++----- Tool/JsScript/package.json | 1 + Tool/JsScript/release.js | 223 ++++++++++++++++++++++------- 4 files changed, 213 insertions(+), 91 deletions(-) create mode 100644 Tool/JsScript/package.json diff --git a/Server/Common/FileDirOp.cs b/Server/Common/FileDirOp.cs index 26ba936..77eb4d2 100644 --- a/Server/Common/FileDirOp.cs +++ b/Server/Common/FileDirOp.cs @@ -211,7 +211,7 @@ public class FileDirOpForUnpack(string srcRootPath, string dstRootPath) : FileDi ZipEntry theEntry; while ((theEntry = s.GetNextEntry()) != null) { - Console.WriteLine(theEntry.Name); + //Console.WriteLine(theEntry.Name); string directoryName = dstPath + $"/{Id}/" + Path.GetDirectoryName(theEntry.Name) diff --git a/Server/RemoteServer/StateHelper.cs b/Server/RemoteServer/StateHelper.cs index b6dfa2c..5730138 100644 --- a/Server/RemoteServer/StateHelper.cs +++ b/Server/RemoteServer/StateHelper.cs @@ -6,7 +6,6 @@ using Common; namespace RemoteServer; - public abstract class StateHelpBase( RemoteSyncServer context, SyncProcessStep step = SyncProcessStep.Connect @@ -132,7 +131,11 @@ public class UnPackAndReleaseHelper(RemoteSyncServer context) Context.Pipe.SendMsg(CreateMsg("解压完成!")).Wait(); var h = new FinallyPublishHelper(Context); Context.SetStateHelpBase(h); - h.FinallyPublish(); + Context.Pipe.SendMsg(h.CreateMsg("将要发布数据库,可能时间会较长!")).Wait(); + Task.Run(() => + { + h.FinallyPublish(); + }); } protected override void HandleMsg(SyncMsg msg) { } @@ -144,42 +147,43 @@ public class FinallyPublishHelper(RemoteSyncServer context) public void FinallyPublish() { // 发布数据库 - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (Context.NotNullSyncConfig.IsDeployDb) + { + var arguments = + $" /Action:Publish /SourceFile:{RemoteSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id.ToString()}/{Context.NotNullSyncConfig.Id}.dacpac" + + $" /TargetServerName:{Context.NotNullSyncConfig.DstDb.ServerName} /TargetDatabaseName:{Context.NotNullSyncConfig.DstDb.DatebaseName}" + + $" /TargetUser:{Context.NotNullSyncConfig.DstDb.User} /TargetPassword:{Context.NotNullSyncConfig.DstDb.Password} /TargetTrustServerCertificate:True"; + + ProcessStartInfo startInfo = + new() + { + StandardOutputEncoding = System.Text.Encoding.UTF8, + Arguments = arguments, + FileName = RemoteSyncServer.SqlPackageAbPath, // The command to execute (can be any command line tool) + // The arguments to pass to the command (e.g., list directory contents) + RedirectStandardOutput = true, // Redirect the standard output to a string + UseShellExecute = false, // Do not use the shell to execute the command + CreateNoWindow = true // Do not create a new window for the command + }; + using Process process = new() { StartInfo = startInfo }; + // Start the process + process.Start(); + + // Read the output from the process + string output = process.StandardOutput.ReadToEnd(); + + // Wait for the process to exit + process.WaitForExit(); + + if (process.ExitCode == 0) { - var arguments = - $" /Action:Publish /SourceFile:{RemoteSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id}/{Context.NotNullSyncConfig.Id}.dacpac" - + $" /TargetServerName:{Context.NotNullSyncConfig.DstDb.ServerName} /TargetDatabaseName:{Context.NotNullSyncConfig.DstDb.DatebaseName}" - + $" /TargetUser:{Context.NotNullSyncConfig.DstDb.User} /TargetPassword:{Context.NotNullSyncConfig.DstDb.Password} /TargetTrustServerCertificate:True"; - - ProcessStartInfo startInfo = - new() - { - StandardOutputEncoding = System.Text.Encoding.UTF8, - FileName = RemoteSyncServer.SqlPackageAbPath, // The command to execute (can be any command line tool) - // The arguments to pass to the command (e.g., list directory contents) - RedirectStandardOutput = true, // Redirect the standard output to a string - UseShellExecute = false, // Do not use the shell to execute the command - CreateNoWindow = true // Do not create a new window for the command - }; - using Process process = new() { StartInfo = startInfo }; - // Start the process - process.Start(); - - // Read the output from the process - string output = process.StandardOutput.ReadToEnd(); - - // Wait for the process to exit - process.WaitForExit(); - - if (process.ExitCode == 0) - { - Context.Pipe.SendMsg(CreateMsg("数据库发布成功!")).Wait(); - } - else - { - Context.Pipe.SendMsg(CreateErrMsg(output)).Wait(); - throw new Exception("执行发布错误,错误信息参考上一条消息!"); - } + Context.Pipe.SendMsg(CreateMsg("数据库发布成功!")).Wait(); + } + else + { + Context.Pipe.SendMsg(CreateErrMsg(output)).Wait(); + throw new Exception("执行发布错误,错误信息参考上一条消息!"); + } } else { diff --git a/Tool/JsScript/package.json b/Tool/JsScript/package.json new file mode 100644 index 0000000..17b4aa2 --- /dev/null +++ b/Tool/JsScript/package.json @@ -0,0 +1 @@ +{ "dependencies": { "chalk": "^5.3.0", "ws": "^8.18.0" } } \ No newline at end of file diff --git a/Tool/JsScript/release.js b/Tool/JsScript/release.js index 4e99fc2..f2da249 100644 --- a/Tool/JsScript/release.js +++ b/Tool/JsScript/release.js @@ -1,44 +1,123 @@ import chalk from "chalk"; import WebSocket from "ws"; - //#region ############################## 配置文件 ################################### +const LocalHost = "127.0.0.1"; //这是个例子,请在`config`中写你的配置 const example_config = { - //发布的项目名称,它的目的是为了防止有两个人同时发布一个项目,和便于排查发布历史记录 - Name: "", - //发布服务器的地址 - RemoteUrl: "http://192.168.1.100:8067", - //源SqlServer数据库的链接字符串,一般是开发或者测试数据库,**此数据库的ip是相对于运行此脚本的机器** - SrcDbConnection: "", - //目的SqlServer数据库的链接字符串,一般是正式环境数据库,**此数据库的ip是相对于运行RemoteServer的机器** - DstDbConnection: "", - - //发布数据库时,只同步结构。此数组中的表,将会连数据也一起同步 - SyncDataTables:[], + //发布的名称,每个项目具有唯一的一个名称 + Name: "Test", + RemotePwd: "t123", + //远程服务器地址,也就是发布的目的地,它是正式环境 + RemoteUrl: "127.0.0.1:6819", + //是否发布数据库 sqlserver + IsDeployDb: false, + //是否发布前重新构建项目 + IsDeployProject: false, + //项目地址 + LocalProjectAbsolutePath: + "D:/git/HMES-H7-HNFY/HMES-H7-HNFYMF/HMES-H7-HNFYMF.WEB", //源文件目录地址,是要发布的文件根目录,它是绝对路径,!执行发布时将发布到这个目录! - LocalRootPath: "", + LocalRootPath: "D:/FileSyncTest/src", //目标文件目录地址,也就是部署服务的机器上的项目文件根目录,它是绝对路径 - RemoteRootPath: "", - - //根目录下的子目录,分子目录是为了针对不同的目录有不同的发布策略,它是相对路径,即相对于LocalRootPath和RemoteRootPath,文件数据依此进行 + RemoteRootPath: "D:/FileSyncTest/dst", + //源数据库配置 SqlServer,将会同步数据库的结构 + SrcDb: { + //Host + ServerName: "172.16.12.2", + //数据库名 + DatebaseName: "HMES_H7_HNFYMF", + User: "hmes-h7", + Password: "Hmes-h7666", + //是否信任服务器证书 + TrustServerCertificate: "True", + //同步的数据,这些数据将会同步 + SyncTablesData: [ + "dbo.sys_Button", + "dbo.sys_Menu", + "dbo.sys_Module", + "dbo.sys_Page", + ], + }, + //目标数据库配置 sqlserver + DstDb: { + ServerName: "127.0.0.1", + DatebaseName: "HMES_H7_HNFYMF", + User: "sa", + Password: "0", + TrustServerCertificate: "True", + }, + //子目录配置,每个子目录都有自己不同的发布策略,它是相对路径,即相对于LocalRootPath和RemoteRootPath(注意 '/',这将拼成一个完整的路径),文件数据依此进行, DirFileConfigs: [ { - //子目录的相对路径 - DirPath: "", + DirPath: "/bin", //排除的文件或目录,它是相对路径,相对于!!!LocalRootPath和RemoteRootPath!!! - Excludes: [], + Excludes: ["/roslyn", "/Views"], //只追踪文件或目录,它是相对路径,相对于!!!LocalRootPath和RemoteRootPath!!!,它的优先级最高,如果你指定了它的值,Excludes将会失效 - CherryPicks: [], + // CherryPicks:[] + }, + ], +}; +const config = { + //发布的名称,每个项目具有唯一的一个名称 + Name: "Test", + RemotePwd: "t123", + //远程服务器地址,也就是发布的目的地,它是正式环境 + RemoteUrl: "127.0.0.1:6819", + //是否发布数据库 sqlserver + IsDeployDb: true, + //是否发布前重新构建项目 + IsDeployProject: true, + //项目地址 + LocalProjectAbsolutePath: + "D:/git/HMES-H7-HNFY/HMES-H7-HNFYMF/HMES-H7-HNFYMF.WEB", + //源文件目录地址,是要发布的文件根目录,它是绝对路径,!执行发布时将发布到这个目录! + LocalRootPath: "D:/FileSyncTest/src", + //目标文件目录地址,也就是部署服务的机器上的项目文件根目录,它是绝对路径 + RemoteRootPath: "D:/FileSyncTest/dst", + //源数据库配置 SqlServer,将会同步数据库的结构 + SrcDb: { + //Host + ServerName: "172.16.12.2", + //数据库名 + DatebaseName: "HMES_H7_HNFYMF", + User: "hmes-h7", + Password: "Hmes-h7666", + //是否信任服务器证书 + TrustServerCertificate: "True", + //同步的数据,这些数据将会同步 + SyncTablesData: [ + "dbo.sys_Button", + "dbo.sys_Menu", + "dbo.sys_Module", + "dbo.sys_Page", + ], + }, + //目标数据库配置 sqlserver + DstDb: { + ServerName: "127.0.0.1", + DatebaseName: "HMES_H7_HNFYMF", + User: "sa", + Password: "0", + TrustServerCertificate: "True", + }, + //子目录配置,每个子目录都有自己不同的发布策略,它是相对路径,即相对于LocalRootPath和RemoteRootPath(注意 '/',这将拼成一个完整的路径),文件数据依此进行, + DirFileConfigs: [ + { + DirPath: "/bin", + //排除的文件或目录,它是相对路径,相对于!!!LocalRootPath和RemoteRootPath!!! + Excludes: ["/roslyn", "/Views"], + //只追踪文件或目录,它是相对路径,相对于!!!LocalRootPath和RemoteRootPath!!!,它的优先级最高,如果你指定了它的值,Excludes将会失效 + // CherryPicks:[] }, ], }; -const config = {}; //#endregion //#region ############################## 打印函数 ################################### +let IsSuccess = false; /** * 在新行打印错误信息 */ @@ -62,9 +141,12 @@ function PrintSuccessInNewLine(str) { /** * 在新行打印一般信息 */ -function PrintGeneralInNewLine(str) { - process.stdout.write("\n"); - process.stdout.write(str); +function PrintCloseNewLine(str) { + if(IsSuccess) { + PrintSuccessInNewLine(str) + } else { + PrintErrInNewLine(str) + } } /** * 在当前行打印一般信息,打印此行信息之前会清除当前行 @@ -80,36 +162,71 @@ function PrintGeneralInCurrentLine(str) { /** * 1-n. localServer 工作,此处只展示信息 */ - -//这个是固定死的 -const wsUrl = `ws://127.0.0.1:4538/websoc?Name=${config.Name}`; -const ws = new WebSocket(wsUrl); - -ws.on('open', () => { - //上传配置 - ws.send(JSON.stringify(config),(err)=>{ - console.log(err) - ws.close() - }) - -}); - -ws.on('message', (message) => { - var msg = message.toString('utf8') - DealMsg(msg) -}); - -ws.on('close', () => { -}); - -function DealMsg(str) { - var msg = JSON.parse(str) - if(msg.IsSuccess) { - +let ws = null; +function MsgCb(MsgIt) { + if (MsgIt.Type == 2) { + PrintGeneralInCurrentLine(MsgIt.Body); + } else { + if (MsgIt.Step <= 6) { + PrintSuccessInNewLine(`(${MsgIt.Step}/6) ${MsgIt.Body}`); + if (MsgIt.Step == 6) { + if (MsgIt.Body == "发布完成!") { + IsSuccess = true + ws.close(); + } + } + } else if(MsgIt == 7) { + PrintErrInNewLine(MsgIt.Body); } else { - PrintErrInNewLine(msg.Body) - ws.close() + PrintCloseNewLine("(关闭)"+ MsgIt.Body); } - + } } //#endregion +async function connectWebSocket() { + return new Promise((resolve, reject) => { + const wsUrl = `ws://${LocalHost}:6818/websoc?Name=${config.Name}`; + ws = new WebSocket(wsUrl); + + ws.onopen = (event) => { + var starter = { + Body: JSON.stringify(config), + Type: 1, + Step: 1, + }; + // console.warn("websocket connected!"); + ws.send(JSON.stringify(starter)); + }; + ws.onmessage = (event) => { + // console.log(event.data); + MsgCb(JSON.parse(event.data)); + }; + ws.onclose = (event) => { + // console.warn(event) + MsgCb({ + Type: 0, + Step: 8, + Body: event.reason, + }); + // resolve() + }; + ws.onerror = (e) => { + // console.error(e); + MsgCb({ + Type: 0, + Body: "异常错误,查看 Console", + Step: 7, + }); + reject(err); + }; + }); +} +(async function main() { + try { + await connectWebSocket(); + // console.log('WebSocket has closed'); + // The script will wait here until the WebSocket connection is closed + } catch (err) { + console.error("Failed to connect or an error occurred:", err); + } +})();