mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 10:54:50 +08:00
orchard.exe now uses CommandHostContextProvider
--HG-- branch : dev
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Web.Hosting;
|
||||
using Orchard;
|
||||
using Orchard.Host;
|
||||
|
||||
namespace OrchardCLI {
|
||||
namespace Orchard.HostContext {
|
||||
public class CommandHostContext {
|
||||
public int StartSessionResult { get; set; }
|
||||
public bool Interactive { get; set; }
|
||||
public int RetryResult { get; set; }
|
||||
public OrchardParameters Arguments { get; set; }
|
||||
public DirectoryInfo OrchardDirectory { get; set; }
|
||||
public int StartSessionResult { get; set; }
|
||||
public bool ShowHelp { get; set; }
|
||||
public ApplicationManager AppManager { get; set; }
|
||||
public ApplicationObject AppObject { get; set; }
|
||||
public CommandHost CommandHost { get; set; }
|
||||
|
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using Orchard;
|
||||
using Orchard.Host;
|
||||
using Orchard.Parameters;
|
||||
|
||||
namespace OrchardCLI {
|
||||
namespace Orchard.HostContext {
|
||||
public class CommandHostContextProvider : ICommandHostContextProvider {
|
||||
private readonly string[] _args;
|
||||
private TextWriter _output;
|
||||
@@ -20,8 +20,9 @@ namespace OrchardCLI {
|
||||
_args = args;
|
||||
}
|
||||
|
||||
public CommandHostContext CreateContext() {
|
||||
public CommandHostContext CreateContext(bool interactive) {
|
||||
var context = new CommandHostContext();
|
||||
context.Interactive = interactive;
|
||||
context.RetryResult = 240;/*special return code for "Retry"*/
|
||||
Initialize(context);
|
||||
return context;
|
||||
@@ -51,18 +52,24 @@ namespace OrchardCLI {
|
||||
// Perform some argument validation and display usage if something is incorrect
|
||||
bool showHelp = context.Arguments.Switches.ContainsKey("?");
|
||||
if (!showHelp) {
|
||||
//showHelp = (!_arguments.Arguments.Any() && !_arguments.ResponseFiles.Any());
|
||||
// If not interactive CLI, we need at least some arguments...
|
||||
if (!context.Interactive) {
|
||||
showHelp = (!context.Arguments.Arguments.Any() && !context.Arguments.ResponseFiles.Any());
|
||||
}
|
||||
}
|
||||
|
||||
if (!showHelp) {
|
||||
//showHelp = (_arguments.Arguments.Any() && _arguments.ResponseFiles.Any());
|
||||
//if (showHelp) {
|
||||
// _output.WriteLine("Incorrect syntax: Response files cannot be used in conjunction with commands");
|
||||
//}
|
||||
// If not interactive CLI, we need
|
||||
if (!context.Interactive) {
|
||||
showHelp = (context.Arguments.Arguments.Any() && context.Arguments.ResponseFiles.Any());
|
||||
if (showHelp) {
|
||||
_output.WriteLine("Incorrect syntax: Response files cannot be used in conjunction with commands");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showHelp) {
|
||||
GeneralHelp();
|
||||
context.ShowHelp = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,58 +94,6 @@ namespace OrchardCLI {
|
||||
context.StartSessionResult = context.CommandHost.StartSession(_input, _output);
|
||||
}
|
||||
|
||||
private int GeneralHelp() {
|
||||
_output.WriteLine("Executes Orchard commands from a Orchard installation directory.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("Usage:");
|
||||
_output.WriteLine(" orchard.exe command [arg1] ... [argn] [/switch1[:value1]] ... [/switchn[:valuen]]");
|
||||
_output.WriteLine(" orchard.exe @response-file1 ... [@response-filen] [/switch1[:value1]] ... [/switchn[:valuen]]");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" command");
|
||||
_output.WriteLine(" Specify the command to execute");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" [arg1] ... [argn]");
|
||||
_output.WriteLine(" Specify additional arguments for the command");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" [/switch1[:value1]] ... [/switchn[:valuen]]");
|
||||
_output.WriteLine(" Specify switches to apply to the command. Available switches generally ");
|
||||
_output.WriteLine(" depend on the command executed, with the exception of a few built-in ones.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" [@response-file1] ... [@response-filen]");
|
||||
_output.WriteLine(" Specify one or more response files to be used for reading commands and switches.");
|
||||
_output.WriteLine(" A response file is a text file that contains one line per command to execute.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" Built-in commands");
|
||||
_output.WriteLine(" =================");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" help commands");
|
||||
_output.WriteLine(" Display the list of available commands.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" help <command-name>");
|
||||
_output.WriteLine(" Display help for a given command.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" Built-in switches");
|
||||
_output.WriteLine(" =================");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /WorkingDirectory:<physical-path>");
|
||||
_output.WriteLine(" /wd:<physical-path>");
|
||||
_output.WriteLine(" Specifies the orchard installation directory. The current directory is the default.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /Verbose");
|
||||
_output.WriteLine(" /v");
|
||||
_output.WriteLine(" Turn on verbose output");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /VirtualPath:<virtual-path>");
|
||||
_output.WriteLine(" /vp:<virtual-path>");
|
||||
_output.WriteLine(" Virtual path to pass to the WebHost. Empty (i.e. root path) by default.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /Tenant:tenant-name");
|
||||
_output.WriteLine(" /t:tenant-name");
|
||||
_output.WriteLine(" Specifies which tenant to run the command into. \"Default\" tenant by default.");
|
||||
_output.WriteLine("");
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void LogInfo(CommandHostContext context, string format, params object[] args) {
|
||||
if (context.Logger != null)
|
||||
context.Logger.LogInfo(format, args);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
namespace OrchardCLI {
|
||||
namespace Orchard.HostContext {
|
||||
public interface ICommandHostContextProvider {
|
||||
CommandHostContext CreateContext();
|
||||
CommandHostContext CreateContext(bool interactive);
|
||||
void Shutdown(CommandHostContext context);
|
||||
}
|
||||
}
|
@@ -1,30 +1,18 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using Orchard.Host;
|
||||
using Orchard.Parameters;
|
||||
using System.Threading;
|
||||
using Orchard.HostContext;
|
||||
|
||||
namespace Orchard {
|
||||
class OrchardHost {
|
||||
private readonly TextReader _input;
|
||||
private readonly TextWriter _output;
|
||||
private readonly string[] _args;
|
||||
private OrchardParameters _arguments;
|
||||
private Logger _logger;
|
||||
private readonly ICommandHostContextProvider _commandHostContextProvider;
|
||||
|
||||
public OrchardHost(TextReader input, TextWriter output, string[] args) {
|
||||
_input = input;
|
||||
_output = output;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
private bool Verbose {
|
||||
get { return _arguments != null && _arguments.Verbose; }
|
||||
_commandHostContextProvider = new CommandHostContextProvider(args);
|
||||
}
|
||||
|
||||
public int Run() {
|
||||
@@ -32,86 +20,46 @@ namespace Orchard {
|
||||
return DoRun();
|
||||
}
|
||||
catch (Exception e) {
|
||||
_output.WriteLine("Error:");
|
||||
_output.WriteLine("Error running command:");
|
||||
for (; e != null; e = e.InnerException) {
|
||||
_output.WriteLine(" {0}", e.Message);
|
||||
if (_logger != null) {
|
||||
_output.WriteLine(" Stacktrace:");
|
||||
_output.WriteLine("{0}", e.StackTrace);
|
||||
_output.WriteLine();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private int DoRun() {
|
||||
_arguments = new OrchardParametersParser().Parse(new CommandParametersParser().Parse(_args));
|
||||
_logger = new Logger(_arguments.Verbose, _output);
|
||||
|
||||
// Perform some argument validation and display usage if something is incorrect
|
||||
bool showHelp = _arguments.Switches.ContainsKey("?");
|
||||
if (!showHelp) {
|
||||
showHelp = (!_arguments.Arguments.Any() && !_arguments.ResponseFiles.Any());
|
||||
var context = CommandHostContext();
|
||||
if (context.ShowHelp) {
|
||||
DisplayHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!showHelp) {
|
||||
showHelp = (_arguments.Arguments.Any() && _arguments.ResponseFiles.Any());
|
||||
if (showHelp) {
|
||||
_output.WriteLine("Incorrect syntax: Response files cannot be used in conjunction with commands");
|
||||
}
|
||||
}
|
||||
|
||||
if (showHelp) {
|
||||
return GeneralHelp();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_arguments.VirtualPath))
|
||||
_arguments.VirtualPath = "/";
|
||||
LogInfo("Virtual path: \"{0}\"", _arguments.VirtualPath);
|
||||
|
||||
if (string.IsNullOrEmpty(_arguments.WorkingDirectory))
|
||||
_arguments.WorkingDirectory = Environment.CurrentDirectory;
|
||||
LogInfo("Working directory: \"{0}\"", _arguments.WorkingDirectory);
|
||||
|
||||
LogInfo("Detecting orchard installation root directory...");
|
||||
var orchardDirectory = GetOrchardDirectory(_arguments.WorkingDirectory);
|
||||
LogInfo("Orchard root directory: \"{0}\"", orchardDirectory.FullName);
|
||||
|
||||
int result = CreateHostAndExecute(orchardDirectory);
|
||||
if (result == 240/*special return code for "Retry"*/)
|
||||
result = CreateHostAndExecute(orchardDirectory);
|
||||
var result = Execute(context);
|
||||
_commandHostContextProvider.Shutdown(context);
|
||||
return result;
|
||||
}
|
||||
|
||||
private int CreateHostAndExecute(DirectoryInfo orchardDirectory) {
|
||||
var appManager = ApplicationManager.GetApplicationManager();
|
||||
|
||||
LogInfo("Creating ASP.NET AppDomain for command agent...");
|
||||
var appObject = CreateWorkerAppDomainWithHost(appManager, _arguments.VirtualPath, orchardDirectory.FullName, typeof(CommandHost));
|
||||
var host = (CommandHost)appObject.ObjectInstance;
|
||||
|
||||
LogInfo("Executing command in ASP.NET AppDomain...");
|
||||
var result = Execute(host);
|
||||
LogInfo("Return code for command: {0}", result);
|
||||
|
||||
LogInfo("Shutting down ASP.NET AppDomain...");
|
||||
appManager.ShutdownApplication(appObject.ApplicationId);
|
||||
|
||||
private CommandHostContext CommandHostContext() {
|
||||
_output.WriteLine("Initializing Orchard host. (This might take a few seconds...)");
|
||||
var result = _commandHostContextProvider.CreateContext(false/*interactive*/);
|
||||
if (result.StartSessionResult == result.RetryResult) {
|
||||
result = _commandHostContextProvider.CreateContext(false/*interactive*/);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int Execute(CommandHost host) {
|
||||
if (_arguments.ResponseFiles.Any()) {
|
||||
var responseLines = new ResponseFiles.ResponseFiles().ReadFiles(_arguments.ResponseFiles);
|
||||
return host.RunCommands(_input, _output, _logger, responseLines.ToArray());
|
||||
private int Execute(CommandHostContext context) {
|
||||
if (context.Arguments.ResponseFiles.Any()) {
|
||||
var responseLines = new ResponseFiles.ResponseFiles().ReadFiles(context.Arguments.ResponseFiles);
|
||||
return context.CommandHost.RunCommands(_input, _output, context.Logger, responseLines.ToArray());
|
||||
}
|
||||
else {
|
||||
return host.RunCommand(_input, _output, _logger, _arguments);
|
||||
return context.CommandHost.RunCommand(_input, _output, context.Logger, context.Arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private int GeneralHelp() {
|
||||
private void DisplayHelp() {
|
||||
_output.WriteLine("Executes Orchard commands from a Orchard installation directory.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("Usage:");
|
||||
@@ -160,64 +108,7 @@ namespace Orchard {
|
||||
_output.WriteLine(" /t:tenant-name");
|
||||
_output.WriteLine(" Specifies which tenant to run the command into. \"Default\" tenant by default.");
|
||||
_output.WriteLine("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void LogInfo(string format, params object[] args) {
|
||||
if (_logger != null)
|
||||
_logger.LogInfo(format, args);
|
||||
}
|
||||
|
||||
private DirectoryInfo GetOrchardDirectory(string directory) {
|
||||
for (var directoryInfo = new DirectoryInfo(directory); directoryInfo != null; directoryInfo = directoryInfo.Parent) {
|
||||
if (!directoryInfo.Exists) {
|
||||
throw new ApplicationException(string.Format("Directory \"{0}\" does not exist", directoryInfo.FullName));
|
||||
}
|
||||
|
||||
// We look for
|
||||
// 1) .\web.config
|
||||
// 2) .\bin\Orchard.Framework.dll
|
||||
var webConfigFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, "web.config"));
|
||||
if (!webConfigFileInfo.Exists)
|
||||
continue;
|
||||
|
||||
var binDirectoryInfo = new DirectoryInfo(Path.Combine(directoryInfo.FullName, "bin"));
|
||||
if (!binDirectoryInfo.Exists)
|
||||
continue;
|
||||
|
||||
var orchardFrameworkFileInfo = new FileInfo(Path.Combine(binDirectoryInfo.FullName, "Orchard.Framework.dll"));
|
||||
if (!orchardFrameworkFileInfo.Exists)
|
||||
continue;
|
||||
|
||||
return directoryInfo;
|
||||
}
|
||||
|
||||
throw new ApplicationException(
|
||||
string.Format("Directory \"{0}\" doesn't seem to contain an Orchard installation", new DirectoryInfo(directory).FullName));
|
||||
}
|
||||
|
||||
private static ApplicationObject CreateWorkerAppDomainWithHost(ApplicationManager appManager, string virtualPath, string physicalPath, Type hostType) {
|
||||
// this creates worker app domain in a way that host doesn't need to be in GAC or bin
|
||||
// using BuildManagerHost via private reflection
|
||||
string uniqueAppString = string.Concat(virtualPath, physicalPath).ToLowerInvariant();
|
||||
string appId = (uniqueAppString.GetHashCode()).ToString("x", CultureInfo.InvariantCulture);
|
||||
|
||||
// create BuildManagerHost in the worker app domain
|
||||
var buildManagerHostType = typeof(HttpRuntime).Assembly.GetType("System.Web.Compilation.BuildManagerHost");
|
||||
var buildManagerHost = appManager.CreateObject(appId, buildManagerHostType, virtualPath, physicalPath, false);
|
||||
|
||||
// call BuildManagerHost.RegisterAssembly to make Host type loadable in the worker app domain
|
||||
buildManagerHostType.InvokeMember(
|
||||
"RegisterAssembly",
|
||||
BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic,
|
||||
null,
|
||||
buildManagerHost,
|
||||
new object[] { hostType.Assembly.FullName, hostType.Assembly.Location });
|
||||
|
||||
// create Host in the worker app domain
|
||||
return new ApplicationObject {
|
||||
ApplicationId = appId,
|
||||
ObjectInstance = appManager.CreateObject(appId, hostType, virtualPath, physicalPath, false) };
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Orchard;
|
||||
using Orchard.HostContext;
|
||||
using Orchard.Parameters;
|
||||
using Orchard.ResponseFiles;
|
||||
|
||||
@@ -17,7 +18,26 @@ namespace OrchardCLI {
|
||||
}
|
||||
|
||||
public int Run() {
|
||||
try {
|
||||
return DoRun();
|
||||
}
|
||||
catch (Exception e) {
|
||||
_output.WriteLine("Error:");
|
||||
for (; e != null; e = e.InnerException) {
|
||||
_output.WriteLine(" {0}", e.Message);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int DoRun() {
|
||||
var context = CommandHostContext();
|
||||
if (context.ShowHelp) {
|
||||
DisplayHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
_output.WriteLine("Type \"help commands\" for help, \"exit\" to exit, \"cls\" to clear screen");
|
||||
while (true) {
|
||||
var command = ReadCommand(context);
|
||||
@@ -46,9 +66,9 @@ namespace OrchardCLI {
|
||||
|
||||
private CommandHostContext CommandHostContext() {
|
||||
_output.WriteLine("Initializing Orchard session. (This might take a few seconds...)");
|
||||
var result = _commandHostContextProvider.CreateContext();
|
||||
var result = _commandHostContextProvider.CreateContext(true/*interactive*/);
|
||||
if (result.StartSessionResult == result.RetryResult) {
|
||||
result = _commandHostContextProvider.CreateContext();
|
||||
result = _commandHostContextProvider.CreateContext(true/*interactive*/);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -78,5 +98,34 @@ namespace OrchardCLI {
|
||||
return context.RetryResult;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayHelp() {
|
||||
_output.WriteLine("Executes the Orchard interactive from a Orchard installation directory.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine("Usage:");
|
||||
_output.WriteLine(" orchardcli.exe [/switch1[:value1]] ... [/switchn[:valuen]]");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" Built-in switches");
|
||||
_output.WriteLine(" =================");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /WorkingDirectory:<physical-path>");
|
||||
_output.WriteLine(" /wd:<physical-path>");
|
||||
_output.WriteLine(" Specifies the orchard installation directory. The current directory is the default.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /Verbose");
|
||||
_output.WriteLine(" /v");
|
||||
_output.WriteLine(" Turn on verbose output");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /VirtualPath:<virtual-path>");
|
||||
_output.WriteLine(" /vp:<virtual-path>");
|
||||
_output.WriteLine(" Virtual path to pass to the WebHost. Empty (i.e. root path) by default.");
|
||||
_output.WriteLine("");
|
||||
_output.WriteLine(" /Tenant:tenant-name");
|
||||
_output.WriteLine(" /t:tenant-name");
|
||||
_output.WriteLine(" Specifies which tenant to run the command into. \"Default\" tenant by default.");
|
||||
_output.WriteLine("");
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user