如何从 FAKE 执行 powershell 脚本 - F#MAKE

How to execute powershell script from FAKE - F#MAKE

我正在将 MSBuild 代码迁移到 FAKE - 我无法从 fake 执行 powershell 脚本,下面是要用 FAKE 编写的 MSBuild 文件的代码:

<Target Name="VersionConfig"> 
    <Exec IgnoreExitCode="false" LogStandardErrorAsError="true" StandardErrorImportance="high" Command="powershell -File &quot;$(BuildRoot)\DeployScripts\scripts\AdminScripts\VersionUpdateFile.ps1&quot;  -path &quot;$(BuildSolutionVersioningConfig)&quot; -majorVersion &quot;$(BuildNumberMajor)&quot; -minor &quot;$(BuildNumberMinor)&quot; -build &quot;$(BuildNumber)&quot; -revision &quot;$(BuildNumberRevision)&quot;"/>
</Target>

如何在 FAKE 中写这个,我是 FAKE 的新手,并没有太多地使用 F#,如果这很明显,请原谅我。

如果有人能提供帮助,那将非常有帮助。

谢谢。

Fake.ProcessHelper namespace in FAKE is what you're looking for. The documentation won't tell you this, but Fake.ProcessHelper is marked with the AutoOpen attribute,这意味着我链接的那个 API 参考页面中列出的所有函数都可以从你的 FAKE 构建脚本中使用,而不需要任何明确的 open使用它们的语句。你这样使用它:

let inQuotes s = sprintf "\"%s\"" s

Target "Sample" (fun _ ->
  let retCode =
    ExecProcess
      (fun info ->
        info.Name <- "powershell.exe"  // Don't know if you need full path here
        info.WorkingDirectory <- getBuildParam "BuildRoot"
        info.Arguments <-
          [ "-File"; getBuildParam "BuildRoot" + "\DeployScripts\scripts\AdminScripts\VersionUpdateFile.ps1" |> inQuotes;
            "-path"; getBuildParam "BuildSolutionVersioningConfig" |> inQuotes;
            "-majorVersion"; getBuildParam "BuildNumberMajor" |> inQuotes;
            "-minor"; getBuildParam "BuildNumberMinor" |> inQuotes;
            "-build"; getBuildParam "BuildNumber" |> inQuotes;
            "-revision"; getBuildParam "BuildNumberRevision" |> inQuotes
          ] |> separated " "
      )
      (TimeSpan.FromMinutes 5.0)
  if retCode <> 0 then
    failwith (sprintf "PowerShell exited with non-zero exit code %d" retCode)
)

几个注意事项:

您可以创建一个 Powershell pipeline in any .NET application with the classes of the System.Management.Automation 命名空间。您可以使用该管道在本地或远程计算机上执行命令。

以下目标检索进程列表并打印它们。它是 PowerShell class:

文档示例的 F# 等价物
Target "ProcTest" (fun _ ->
    PowerShell.Create()    
            .AddCommand("get-process")
            .Invoke()
            |> Seq.map(fun result->(  result.Properties.["ProcessName"].Value,
                                      result.Properties.["ID"].Value))
            |> Seq.iter (fun (name,id)->printfn "%-24O %O" name id)
)

这是我用来在远程服务器上执行命令(特别是巧克力命令)的快速而肮脏的模块。让它在本地机器上运行的唯一需要做的就是 删除 ComputerName 参数:

#r "System.Management.Automation"

module MyPowershell =

    let InvokeRemote server command =
        let block = ScriptBlock.Create(command)
        let pipe=PowerShell.Create()    
                .AddCommand("invoke-command")
        pipe.AddParameter("ComputerName", server)            
            .AddParameter("ScriptBlock", block)
            .Invoke() 
            |> Seq.map (sprintf  "%O")
            |> Seq.iter (fun line ->
                                let tracer=if line.Contains("not installed") then
                                                traceError 
                                        else 
                                                trace
                                tracer line)
        pipe.Streams.Error |> Seq.iter (traceError << sprintf "%O" )

管道由 PowerShell class 表示。构建它的步骤是:

  1. 使用 PowerShell.Create()
  2. 创建 PowerShell 管道
  3. 使用您要执行的命令创建一个 ScriptBlock。您不需要所有命令的 ScriptBlock。在这种情况下,ScriptBlock 包含我要远程执行的脚本。
  4. 添加您要执行的内容。就我而言,我想 运行 一个 invoke-command 命令
  5. 添加参数。在这种情况下,它是 -ComputerName myServer-ScriptBlock ... 与要执行的块。
  6. 运行 管道 PowerShell.Invoke()
  7. 分析未进入错误流的可能警告消息的结果

PowerShell 错误被发送到 Error 流,这使得错误处理更容易。

在本地机器上执行命令会简单很多:

let InvokeRemote command =
    let pipe=PowerShell.Create()    
            .AddCommand(command)
    pipe.Invoke() 
        |> Seq.map (sprintf  "%O")
        |> Seq.iter (fun line ->
                            let tracer=if line.Contains("not installed") then
                                            traceError 
                                    else 
                                            trace
                            tracer line)
    pipe.Streams.Error |> Seq.iter (traceError << sprintf "%O" )