From e22e0e714b14b22945adb6df5ef6cd8c51e7883a Mon Sep 17 00:00:00 2001 From: Cno Date: Sun, 12 Nov 2023 01:25:24 +0800 Subject: [PATCH] =?UTF-8?q?doc:=20=E6=B7=BB=E5=8A=A0=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/nep/definition/4-steps.mdx | 47 +++++++++++++++++++++++++++--- scripts/step/markdown.ts | 2 +- src/types/steps/copy.rs | 3 ++ src/types/steps/delete.rs | 2 ++ src/types/steps/execute.rs | 53 +++++++++++++++------------------- src/types/steps/kill.rs | 3 +- src/types/steps/link.rs | 4 +++ src/types/steps/mv.rs | 3 ++ src/types/steps/new.rs | 1 + src/types/steps/path.rs | 1 + src/types/steps/rename.rs | 3 ++ src/types/steps/wait.rs | 2 ++ 12 files changed, 88 insertions(+), 36 deletions(-) diff --git a/doc/nep/definition/4-steps.mdx b/doc/nep/definition/4-steps.mdx index c3f32c7c..7a1fcb88 100644 --- a/doc/nep/definition/4-steps.mdx +++ b/doc/nep/definition/4-steps.mdx @@ -19,10 +19,15 @@ import { Tag } from "../../components/tag.tsx" # 绝对路径通配符写法 from = "${SystemDrive}/Windows/system32/*.dll" ``` +* 校验规则: + * 是合法路径 + * 符合通配符用法 #### to 目标路径,支持相对路径和绝对路径。 * 类型:`String` * 示例:`to = "./config"` +* 校验规则: + * 是合法路径 #### overwrite 可选 是否覆盖,缺省为 `false`。 * 类型:`bool` @@ -41,6 +46,9 @@ import { Tag } from "../../components/tag.tsx" # 绝对路径通配符写法 at = "${AppData}/vscode/*.txt" ``` +* 校验规则: + * 是合法路径 + * 符合通配符用法 #### force 可选 是否强制删除,缺省为 `false`。 * 类型:`bool` @@ -52,10 +60,15 @@ import { Tag } from "../../components/tag.tsx" 需要执行的命令,使用终端为 cmd。 * 类型:`String` * 示例:`command = "./installer.exe /S"` +* 校验规则: + * 是有效的 POSIX 命令 + * 不得出现绝对路径(使用[内置变量](/nep/workflow/2-context.html#内置变量)) #### pwd 可选 执行目录,缺省为包安装目录。 * 类型:`String` * 示例:`pwd = "${AppData}/Microsoft"` +* 校验规则: + * 是合法路径 #### call_installer 可选 当前命令的语义是否为正在调用安装器,缺省为 `false`;请务必正确指定此项,因为这会影响包权限、工作流静态检查等行为。 * 类型:`bool` @@ -75,9 +88,11 @@ import { Tag } from "../../components/tag.tsx" 杀死某个进程。 ### 字段 #### target -进程名称,大小写敏感且必须以 `.exe` 结尾。 +进程名称,注意大小写敏感。 * 类型:`String` -* 示例:`target = "code.exe"` +* 示例:`target = "code.exe"` +* 校验规则: + * 以 `.exe` 结尾 ## Link 创建快捷方式。 ### 字段 @@ -85,6 +100,8 @@ import { Tag } from "../../components/tag.tsx" 源文件路径,支持相对路径和绝对路径。 * 类型:`String` * 示例:`source_file = "code.exe"` +* 校验规则: + * 是合法路径 #### target_name 可选 快捷方式名称,支持使用 `FOLDER/NAME` 的模式表示在创建位置的文件夾中放置快捷方式。 * 类型:`String` @@ -96,6 +113,10 @@ import { Tag } from "../../components/tag.tsx" # 在文件夹中创建快捷方式 target_name = "Microsoft/Visual Studio Code" ``` +* 校验规则: + * 符合模式 `NAME` 或 `FOLDER/NAME` + * 不包含 `..` + * 不以 `.lnk` 结尾 #### target_args 可选 快捷方式的启动参数。 * 类型:`String` @@ -137,10 +158,15 @@ import { Tag } from "../../components/tag.tsx" # 绝对路径通配符写法 from = "${Desktop}/Visual Studio Code.lnk" ``` +* 校验规则: + * 是合法路径 + * 符合通配符用法 #### to 目标路径,支持相对路径和绝对路径。 * 类型:`String` * 示例:`to = "./bin"` +* 校验规则: + * 是合法路径 #### overwrite 可选 是否覆盖,缺省为 `false`。 * 类型:`bool` @@ -159,6 +185,8 @@ import { Tag } from "../../components/tag.tsx" # 创建文件夹 at = "${Desktop}/Microsoft/" ``` +* 校验规则: + * 是合法路径 #### overwrite 可选 是否覆盖,缺省为 false。 * 类型:`bool` @@ -179,6 +207,8 @@ import { Tag } from "../../components/tag.tsx" # 添加文件夹到 PATH 变量 record = "./bin" ``` +* 校验规则: + * 是合法路径 #### alias 可选 别名,仅对可执行文件生效,缺省为原文件名。 * 类型:`String` @@ -192,10 +222,15 @@ import { Tag } from "../../components/tag.tsx" 目标路径,支持相对路径和绝对路径。 * 类型:`String` * 示例:`from = "./config.toml.example"` +* 校验规则: + * 是合法路径 + * 不包含通配符 #### to 新的名称。 * 类型:`String` -* 示例:`to = "config.toml"` +* 示例:`to = "config.toml"` +* 校验规则: + * 是合法的文件名或文件夹名而非路径 ## Toast 弹出消息通知。 ### 字段 @@ -214,7 +249,11 @@ import { Tag } from "../../components/tag.tsx" 等待的时长,单位为 ms。 * 类型:`u64` * 示例:`timeout = "3000"` +* 校验规则: + * 不超过 30min(1800000ms) #### break_if 可选 若满足指定条件则提前结束等待,该条件会在等待过程中每 500ms 检查一次。 * 类型:`String` -* 示例:`break_if = 'Exist("${Desktop}/Visual Studio Code.lnk")'` \ No newline at end of file +* 示例:`break_if = 'Exist("${Desktop}/Visual Studio Code.lnk")'` +* 校验规则: + * 是合法的条件 \ No newline at end of file diff --git a/scripts/step/markdown.ts b/scripts/step/markdown.ts index d6869fae..d56af8fd 100644 --- a/scripts/step/markdown.ts +++ b/scripts/step/markdown.ts @@ -20,7 +20,7 @@ function fieldRenderer( : ""; const demoText = field.demo ? `\n* 示例:${field.demo}` : ""; const rulesText = field.rules?.length - ? "\n" + field.rules.map((rule) => `* ${rule}`).join("\n") + ? "\n* 校验规则:\n" + field.rules.map((rule) => ` * ${rule}`).join("\n") : ""; return `${"#".repeat(titleLevel)} ${field.name} ${field.type.optional ? "可选 " : ""}${field.wiki ?? ""} diff --git a/src/types/steps/copy.rs b/src/types/steps/copy.rs index 0eebab07..869f905c 100644 --- a/src/types/steps/copy.rs +++ b/src/types/steps/copy.rs @@ -30,9 +30,12 @@ pub struct StepCopy { //# # 绝对路径通配符写法 //# from = "${SystemDrive}/Windows/system32/*.dll" //# ``` + //@ 是合法路径 + //@ 符合通配符用法 pub from: String, /// 目标路径,支持相对路径和绝对路径。 //# `to = "./config"` + //@ 是合法路径 pub to: String, /// 是否覆盖,缺省为 `false`。 //# `overwrite = true` diff --git a/src/types/steps/delete.rs b/src/types/steps/delete.rs index bd1ee755..e5cf7fd5 100644 --- a/src/types/steps/delete.rs +++ b/src/types/steps/delete.rs @@ -29,6 +29,8 @@ pub struct StepDelete { //# # 绝对路径通配符写法 //# at = "${AppData}/vscode/*.txt" //# ``` + //@ 是合法路径 + //@ 符合通配符用法 pub at: String, /// 是否强制删除,缺省为 `false`。 //# `force = "true"` diff --git a/src/types/steps/execute.rs b/src/types/steps/execute.rs index 25109514..7d705946 100644 --- a/src/types/steps/execute.rs +++ b/src/types/steps/execute.rs @@ -1,3 +1,4 @@ +use crate::executor::{values_replacer, values_validator_path}; use crate::types::interpretable::Interpretable; use crate::types::mixed_fs::MixedFS; use crate::types::permissions::{Generalizable, Permission, PermissionKey, PermissionLevel}; @@ -19,9 +20,12 @@ use std::time::Instant; pub struct StepExecute { /// 需要执行的命令,使用终端为 cmd。 //# `command = "./installer.exe /S"` + //@ 是有效的 POSIX 命令 + //@ 不得出现绝对路径(使用[内置变量](/nep/workflow/2-context.html#内置变量)) pub command: String, /// 执行目录,缺省为包安装目录。 //# `pwd = "${AppData}/Microsoft"` + //@ 是合法路径 pub pwd: Option, /// 当前命令的语义是否为正在调用安装器,缺省为 `false`;请务必正确指定此项,因为这会影响包权限、工作流静态检查等行为。 //# `call_installer = true` @@ -58,8 +62,11 @@ impl TStep for StepExecute { self.command = format!("\"{c}\"", c = &self.command); } + // 解释内置变量 + let command_str = values_replacer(self.command, cx.exit_code, &cx.located); + // 解析命令传入 - let cmd = c.args(split_command(&self.command)?); + let cmd = c.args(split_command(&command_str)?); // 指定工作目录 let workshop = self.pwd.unwrap_or(cx.located.to_owned()); @@ -72,16 +79,10 @@ impl TStep for StepExecute { let wait = self.wait.unwrap_or("Sync".to_string()); if wait == "Sync".to_string() { // 同步执行并收集结果 - log!( - "Info(Execute):Running sync command '{cmd}' in '{workshop}'", - cmd = self.command, - ); + log!("Info(Execute):Running sync command '{command_str}' in '{workshop}'"); let start_instant = Instant::now(); let output = cmd.output().map_err(|err| { - anyhow!( - "Error(Execute):Command '{cmd}' execution failed : {err}", - cmd = self.command, - ) + anyhow!("Error(Execute):Command '{command_str}' execution failed : {err}") })?; // 如果在调用安装器,检查是否过快退出 @@ -99,15 +100,11 @@ impl TStep for StepExecute { match output.status.code() { Some(val) => { if val == 0 { - log!( - "{level}(Execute):Command '{cmd}' {hint}, output :", - cmd = self.command - ); + log!("{level}(Execute):Command '{command_str}' {hint}, output :"); println!("{output}", output = read_console(output.stdout)); } else { log!( - "Error(Execute):Failed command '{cmd}' {hint}, output(code={val}) : \n{o}", - cmd = self.command, + "Error(Execute):Failed command '{command_str}' {hint}, output(code={val}) : \n{o}", o = read_console(output.stderr) ); println!("{output}", output = read_console(output.stdout)); @@ -115,27 +112,17 @@ impl TStep for StepExecute { Ok(val) } None => Err(anyhow!( - "Error(Execute):Command '{cmd}' terminated by outer signal", - cmd = self.command + "Error(Execute):Command '{command_str}' terminated by outer signal" )), } } else { // 异步执行 - log!( - "Info(Execute):Running async command('{wait}') '{cmd}' in '{workshop}'", - cmd = self.command, - ); + log!("Info(Execute):Running async command('{wait}') '{command_str}' in '{workshop}'"); let handler = cmd.spawn().map_err(|e| { - anyhow!( - "Error(Execute):Command '{cmd}' spawn failed : {e}", - cmd = self.command - ) + anyhow!("Error(Execute):Command '{command_str}' spawn failed : {e}") })?; - cx.async_execution_handlers.push(( - self.command, - handler, - wait == "Abandon".to_string(), - )); + cx.async_execution_handlers + .push((command_str, handler, wait == "Abandon".to_string())); Ok(0) } @@ -177,6 +164,12 @@ impl Interpretable for StepExecute { impl Verifiable for StepExecute { fn verify_self(&self, _: &String) -> Result<()> { + // 校验 pwd 为合法路径 + if let Some(pwd) = &self.pwd { + values_validator_path(pwd)?; + } + + // 校验命令前进行路径格式化 let formatted_cmd = format_path(&self.command); // 禁止出现 :/ diff --git a/src/types/steps/kill.rs b/src/types/steps/kill.rs index f7d0af17..d3df63f0 100644 --- a/src/types/steps/kill.rs +++ b/src/types/steps/kill.rs @@ -17,8 +17,9 @@ use sysinfo::{ProcessExt, System, SystemExt}; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct StepKill { - /// 进程名称,大小写敏感且必须以 `.exe` 结尾。 + /// 进程名称,注意大小写敏感。 //# `target = "code.exe"` + //@ 以 `.exe` 结尾 pub target: String, } diff --git a/src/types/steps/link.rs b/src/types/steps/link.rs index 766536d9..20b0aebc 100644 --- a/src/types/steps/link.rs +++ b/src/types/steps/link.rs @@ -111,6 +111,7 @@ fn update_start_menu() { pub struct StepLink { /// 源文件路径,支持相对路径和绝对路径。 //# `source_file = "code.exe"` + //@ 是合法路径 pub source_file: String, /// 快捷方式名称,支持使用 `FOLDER/NAME` 的模式表示在创建位置的文件夾中放置快捷方式。 //# ```toml @@ -120,6 +121,9 @@ pub struct StepLink { //# # 在文件夹中创建快捷方式 //# target_name = "Microsoft/Visual Studio Code" //# ``` + //@ 符合模式 `NAME` 或 `FOLDER/NAME` + //@ 不包含 `..` + //@ 不以 `.lnk` 结尾 pub target_name: Option, /// 快捷方式的启动参数。 //# `target_args = "--debug"` diff --git a/src/types/steps/mv.rs b/src/types/steps/mv.rs index b02408fc..76f37064 100644 --- a/src/types/steps/mv.rs +++ b/src/types/steps/mv.rs @@ -26,9 +26,12 @@ pub struct StepMove { //# # 绝对路径通配符写法 //# from = "${Desktop}/Visual Studio Code.lnk" //# ``` + //@ 是合法路径 + //@ 符合通配符用法 pub from: String, /// 目标路径,支持相对路径和绝对路径。 //# `to = "./bin"` + //@ 是合法路径 pub to: String, /// 是否覆盖,缺省为 `false`。 //# `overwrite = "true"` diff --git a/src/types/steps/new.rs b/src/types/steps/new.rs index e16c34c8..ea14327a 100644 --- a/src/types/steps/new.rs +++ b/src/types/steps/new.rs @@ -26,6 +26,7 @@ pub struct StepNew { //# # 创建文件夹 //# at = "${Desktop}/Microsoft/" //# ``` + //@ 是合法路径 pub at: String, /// 是否覆盖,缺省为 false。 //# `overwrite = "true"` diff --git a/src/types/steps/path.rs b/src/types/steps/path.rs index 14fd6230..d746b356 100644 --- a/src/types/steps/path.rs +++ b/src/types/steps/path.rs @@ -30,6 +30,7 @@ pub struct StepPath { //# # 添加文件夹到 PATH 变量 //# record = "./bin" //# ``` + //@ 是合法路径 pub record: String, /// 别名,仅对可执行文件生效,缺省为原文件名。 //# `alias = "code.exe"` diff --git a/src/types/steps/rename.rs b/src/types/steps/rename.rs index 1509b276..e79cb84d 100644 --- a/src/types/steps/rename.rs +++ b/src/types/steps/rename.rs @@ -29,9 +29,12 @@ lazy_static! { pub struct StepRename { /// 目标路径,支持相对路径和绝对路径。 //# `from = "./config.toml.example"` + //@ 是合法路径 + //@ 不包含通配符 pub from: String, /// 新的名称。 //# `to = "config.toml"` + //@ 是合法的文件名或文件夹名而非路径 pub to: String, } diff --git a/src/types/steps/wait.rs b/src/types/steps/wait.rs index 7bf515b1..afd644b8 100644 --- a/src/types/steps/wait.rs +++ b/src/types/steps/wait.rs @@ -16,9 +16,11 @@ use std::{thread::sleep, time::Duration}; pub struct StepWait { /// 等待的时长,单位为 ms。 //# `timeout = "3000"` + //@ 不超过 30min(1800000ms) pub timeout: u64, /// 若满足指定条件则提前结束等待,该条件会在等待过程中每 500ms 检查一次。 //# `break_if = 'Exist("${Desktop}/Visual Studio Code.lnk")'` + //@ 是合法的条件 pub break_if: Option, }