/// <summary> /// Builds the ClickOnce manifest for the client, using the configuration from env.xml and manifest templates from the Templates directory. /// </summary> /// <param name="envConfig"></param> internal static void BuildClickOnce(EnvConfig envConfig) { if (envConfig.ClickOnce == null) return; var origCurrDir = Environment.CurrentDirectory; try { Program.LogTitle("Creating ClickOnce manifest"); Environment.CurrentDirectory = envConfig.BinaryTarget; Program.LogDetail("Changed CWD to BinaryTarget: [{0}]", Environment.CurrentDirectory); var appId = new AppId() { name = envConfig.ClickOnce.Product + ".exe", version = envConfig.ClickOnce.DeploymentVersion, publicKeyToken = FormatKey(PublicKeyTokenFromPfx(envConfig.ClickOnce.KeyFile)), language = "neutral", processorArchitecture = "bar", type = "win32", }; CreateClickOnceManifest(envConfig, appId); Sign(envConfig, appId, GetManifestTemplateName(envConfig), GetManifestName(envConfig)); CreateClickOnceAppplication(envConfig, appId); Sign(envConfig, appId, GetAppTemplateName(envConfig), GetAppName(envConfig)); } finally { Environment.CurrentDirectory = origCurrDir; Program.LogDetail("Changed CWD back to [{0}]", Environment.CurrentDirectory); } }
private static void DeployDatabaseTemplate(EnvConfig envConfig) { if (envConfig.DatabaseSource == null || envConfig.DatabaseTarget == null) { return; } LogTitle("Deploying Database Template"); // Delete Target using (var schemaProvider = SchemaProvider.SchemaProviderFactory.CreateInstance(envConfig.DatabaseTarget.Schema)) { LogAction("drop target database contents"); schemaProvider.Open(envConfig.DatabaseTarget.Value); schemaProvider.DropAllObjects(); } if (envConfig.DatabaseSource.Schema != "EMPTY" && !string.IsNullOrEmpty(envConfig.DatabaseSource.Value) && !string.IsNullOrEmpty(envConfig.DatabaseTarget.Value)) { // Copy database using (var schemaProvider = SchemaProvider.SchemaProviderFactory.CreateInstance(envConfig.DatabaseTarget.Schema)) { LogAction("copying database"); schemaProvider.Copy(envConfig.DatabaseSource.Value, envConfig.DatabaseTarget.Value); } } }
private static void EnforceAppServer(EnvConfig envConfig) { if (envConfig.AppServer == null || string.IsNullOrEmpty(envConfig.AppServer.Uri)) { return; } LogTitle("Enforcing app server"); foreach (var configPath in GetAppconfigFilenames(envConfig.BinaryTarget) .Concat(GetAppconfigFilenames(envConfig.TestsTarget))) { var doc = new XmlDocument(); doc.Load(configPath); var endpoints = doc.SelectNodes("/configuration/system.serviceModel/client/endpoint"); foreach (XmlElement endpoint in endpoints) { endpoint.Attributes["address"].Value = envConfig.AppServer.Uri; LogAction("set endpoint address in {0}", configPath); } var serviceUris = doc.SelectNodes("/configuration/appSettings/add[@key='serviceUri']"); foreach (XmlElement serviceUri in serviceUris) { serviceUri.Attributes["value"].Value = envConfig.AppServer.Uri; LogAction("set serviceUri address in {0}", configPath); } doc.Save(configPath); } }
/// <summary> /// forces all configuration's connection string to be envConfig.DatabaseTarget /// </summary> /// <param name="envConfig"></param> private static void EnforceConnectionString(EnvConfig envConfig) { if (envConfig.DatabaseTarget == null) { return; } LogTitle("Enforcing connection string"); foreach (var configPath in GetConfigFilenames(envConfig.BinaryTarget) .Concat(GetConfigFilenames(envConfig.TestsTarget))) { var doc = new XmlDocument(); doc.Load(configPath); // create prefix<->namespace mappings (if any) var nsMgr = new XmlNamespaceManager(doc.NameTable); nsMgr.AddNamespace("k", "http://dasz.at/Zetbox/"); // check whether this is a ZetboxConfig var configSet = doc.SelectNodes("/k:ZetboxConfig", nsMgr); if (configSet.Count == 0) { continue; // nope, ignore! } var serverNode = doc.SelectSingleNode("/k:ZetboxConfig/k:Server[@StartServer='true']", nsMgr); if (serverNode == null) { continue; // no startable server, ignore! } // Select a database called "Zetbox" var databaseNode = doc.SelectSingleNode("/k:ZetboxConfig/k:Server[@StartServer=true]/k:ConnectionStrings/k:Database[@Name=Zetbox]", nsMgr); if (databaseNode == null) { databaseNode = doc.CreateElement("Database", "http://dasz.at/Zetbox/"); var connectionStringsNode = doc.SelectSingleNode("/k:ZetboxConfig/k:Server[@StartServer=true]/k:ConnectionStrings", nsMgr); if (connectionStringsNode == null) { connectionStringsNode = doc.CreateElement("ConnectionStrings", "http://dasz.at/Zetbox/"); serverNode.PrependChild(connectionStringsNode); } connectionStringsNode.PrependChild(databaseNode); } EnsureAttribute(doc, databaseNode, "Name"); databaseNode.Attributes["Name"].Value = "Zetbox"; EnsureAttribute(doc, databaseNode, "Schema"); databaseNode.Attributes["Schema"].Value = envConfig.DatabaseTarget.Schema; EnsureAttribute(doc, databaseNode, "Provider"); databaseNode.Attributes["Provider"].Value = envConfig.DatabaseTarget.Provider; databaseNode.InnerText = envConfig.DatabaseTarget.Value; LogAction("set connection string in {0}", configPath); doc.Save(configPath); } }
/// <summary> /// copy configs from envConfig.ConfigSource to envConfig.BinaryTarget\Configs; /// deploy app.configs to their proper resting place beside the executable /// </summary> /// <param name="envConfig"></param> private static void InstallTestsConfigs(EnvConfig envConfig) { LogTitle("Installing Tests Configs"); if (!string.IsNullOrEmpty(envConfig.TestsTarget) && !string.IsNullOrEmpty(envConfig.ConfigSource)) { var configTargetDir = Path.Combine(envConfig.TestsTarget, "Configs"); // copy all configs CopyFolder(envConfig.ConfigSource, configTargetDir); DeployConfigs(configTargetDir, envConfig.TestsTarget); } }
/// <summary> /// copy configs from envConfig.ConfigSource to envConfig.BinaryTarget\Configs; /// deploy app.configs to their proper resting place beside the executable /// </summary> /// <param name="envConfig"></param> private static void InstallConfigs(EnvConfig envConfig) { LogTitle("Installing Configs"); if (!string.IsNullOrEmpty(envConfig.BinaryTarget)) { var configTargetDir = Path.Combine(envConfig.BinaryTarget, "Configs"); // copy all configs CopyFolder(envConfig.ConfigSource, configTargetDir); // find all app.configs DeployConfigs(configTargetDir, envConfig.BinaryTarget); } }
/// <summary> /// Copy from envConfig.BinarySource to envConfig.TestsTarget /// </summary> /// <param name="envConfig"></param> private static void InstallTestsBinaries(EnvConfig envConfig) { LogTitle("Installing Tests Binaries"); // if source is empty or source and target are the same, binaries do not have to be copied if (!string.IsNullOrEmpty(envConfig.TestsTarget) && !string.IsNullOrEmpty(envConfig.BinarySource) && envConfig.BinarySource != envConfig.TestsTarget) { var sourcePaths = ExpandPath(envConfig.BinarySource); var isWildcard = sourcePaths.Count() > 1; foreach (var source in sourcePaths) { LogAction("copying Binaries from " + source); if (isWildcard && !Directory.Exists(source)) { continue; } CopyFolder(source, envConfig.TestsTarget, "*.dll", CopyMode.Flat); // does not handle sattelite assemblies CopyFolder(source, envConfig.TestsTarget, "*.dll.config", CopyMode.Flat); CopyFolder(source, envConfig.TestsTarget, "*.pdb", CopyMode.Flat); CopyFolder(source, envConfig.TestsTarget, "*.mdb", CopyMode.Flat); } // Replace fallback binaries when generated ones are available foreach (var generatedSource in new[] { PathX.Combine(envConfig.BinarySource, "Common", "Core.Generated"), PathX.Combine(envConfig.BinarySource, "Client", "Core.Generated"), PathX.Combine(envConfig.BinarySource, "Server", "EF.Generated"), PathX.Combine(envConfig.BinarySource, "Server", "NH.Generated") }) { var generatedSourcePaths = ExpandPath(generatedSource); foreach (var path in generatedSourcePaths) { LogDetail("looking at [{0}]", path); if (Directory.Exists(path)) { LogDetail("found"); CopyTopFiles(path, envConfig.TestsTarget); } else { LogDetail("not found"); } } } ReplaceNpgsql(envConfig.BinarySource, envConfig.TestsTarget); } }
/// <summary> /// Copy from envConfig.BinarySource to envConfig.BinaryTarget; fetch files from Modules\ and Data\ /// </summary> /// <param name="envConfig"></param> private static void InstallBinaries(EnvConfig envConfig) { LogTitle("Installing Binaries"); // if source is empty or source and target are the same, binaries do not have to be copied if (!string.IsNullOrEmpty(envConfig.BinarySource) && !string.IsNullOrEmpty(envConfig.BinaryTarget) && envConfig.BinarySource != envConfig.BinaryTarget) { var sourcePaths = ExpandPath(envConfig.BinarySource); var isWildcard = sourcePaths.Count() > 1; foreach (var source in sourcePaths) { LogAction("copying Binaries from " + source); if (isWildcard && !Directory.Exists(source)) { continue; } CopyFolder(source, envConfig.BinaryTarget); var bootstrapperSource = Path.Combine(source, "Bootstrapper"); if (Directory.Exists(bootstrapperSource)) { // Bootstrapper has to be available in the web root CopyFolder(bootstrapperSource, PathX.Combine(envConfig.BinaryTarget, "HttpService", "Bootstrapper")); } } var moduleTarget = Path.Combine(envConfig.BinaryTarget, "Modules"); if (Directory.Exists("Modules")) { LogAction("copying Modules"); CopyFolder("Modules", moduleTarget); } var dataTarget = Path.Combine(envConfig.BinaryTarget, "Data"); if (Directory.Exists("Data")) { LogAction("copying Data"); CopyFolder("Data", dataTarget); } ReplaceNpgsql(envConfig.BinarySource, envConfig.BinaryTarget); } }
private static void PrepareEnvConfig(EnvConfig envConfig, string envConfigDir) { if (envConfig.AppServer != null) { envConfig.AppServer.Type = ExpandEnvVars(envConfig.AppServer.Type); envConfig.AppServer.Uri = ExpandEnvVars(envConfig.AppServer.Uri); } envConfig.BinarySource = PrepareConfigPath(envConfig.BinarySource); envConfig.BinaryTarget = PrepareConfigPath(envConfig.BinaryTarget); envConfig.TestsTarget = PrepareConfigPath(envConfig.TestsTarget); if (string.IsNullOrEmpty(envConfig.ConfigSource)) { envConfig.ConfigSource = envConfigDir; } else { envConfig.ConfigSource = PrepareConfigPath(envConfig.ConfigSource); } if (envConfig.DatabaseSource != null) { envConfig.DatabaseSource.Provider = ExpandEnvVars(envConfig.DatabaseSource.Provider); envConfig.DatabaseSource.Schema = ExpandEnvVars(envConfig.DatabaseSource.Schema); envConfig.DatabaseSource.Value = ExpandEnvVars(envConfig.DatabaseSource.Value); } if (envConfig.DatabaseTarget != null) { envConfig.DatabaseTarget.Provider = ExpandEnvVars(envConfig.DatabaseTarget.Provider); envConfig.DatabaseTarget.Schema = ExpandEnvVars(envConfig.DatabaseTarget.Schema); envConfig.DatabaseTarget.Value = ExpandEnvVars(envConfig.DatabaseTarget.Value); } }
private static void Sign(EnvConfig envConfig, AppId appId, string templateName, string outputName) { switch (Environment.OSVersion.Platform) { case PlatformID.Unix: SignXmlSec1(envConfig, templateName, outputName, appId); break; case PlatformID.Win32NT: SignMage(envConfig, templateName, outputName); break; default: throw new NotSupportedException(string.Format("Signing on platform '{0}' is not supported", Environment.OSVersion.Platform)); } }
private static void InsertClickOnceDependency(EnvConfig envConfig, XmlNode dependencyList, XmlNode lastPrerequisite, string file, XmlNamespaceManager nsmgr) { var deployTarget = Path.Combine(GetClickOnceOutputPath(envConfig), file); var doc = dependencyList.OwnerDocument; // avoid the pseudo/testing resource assemblies, as clickonce client dies on unknown cultures if (file.ToLowerInvariant().EndsWith(".resources.dll") || file.ToLowerInvariant().EndsWith(".resources.dll.deploy") || file.ToLowerInvariant().EndsWith(".resources.exe") || file.ToLowerInvariant().EndsWith(".resources.exe.deploy")) { if (file.ToLowerInvariant().Contains("x-zb-")) { Program.LogDetail("Skipping pseudo culture file: [{0}]", file); return; } } if (file.EndsWith(".dll") || file.EndsWith(".dll.deploy") || file.EndsWith(".exe") || file.EndsWith(".exe.deploy")) { // TODO: do not deploy fallback to client and remove this. if (file.Contains("Fallback")) return; var dependency = doc.CreateNode(XmlNodeType.Element, "dependency", ASMv2_NS); var dependentAssembly = doc.CreateNode(XmlNodeType.Element, "dependentAssembly", ASMv2_NS); SetOrReplaceAttribute(dependentAssembly, "dependencyType", null, "install"); SetOrReplaceAttribute(dependentAssembly, "allowDelayedBinding", null, "true"); SetOrReplaceAttribute(dependentAssembly, "codebase", null, file.Replace('/', '\\')); SetOrReplaceAttribute(dependentAssembly, "size", null, string.Format(CultureInfo.InvariantCulture, "{0}", new FileInfo(file).Length)); var assemblyIdentity = doc.CreateNode(XmlNodeType.Element, "assemblyIdentity", ASMv2_NS); FillClickOnceAssemblyId(System.Reflection.Assembly.ReflectionOnlyLoadFrom(file), assemblyIdentity); dependentAssembly.AppendChild(assemblyIdentity); var hash = CreateHashNode(file, nsmgr, doc); dependentAssembly.AppendChild(hash); dependency.AppendChild(dependentAssembly); dependencyList.InsertAfter(dependency, lastPrerequisite); deployTarget += ".deploy"; } else if (file.EndsWith(".manifest")) { var dependency = doc.CreateNode(XmlNodeType.Element, "dependency", ASMv2_NS); var dependentAssembly = doc.CreateNode(XmlNodeType.Element, "dependentAssembly", ASMv2_NS); SetOrReplaceAttribute(dependentAssembly, "dependencyType", null, "install"); SetOrReplaceAttribute(dependentAssembly, "codebase", null, file.Replace('/', '\\')); SetOrReplaceAttribute(dependentAssembly, "size", null, string.Format(CultureInfo.InvariantCulture, "{0}", new FileInfo(file).Length)); var manifest = new XmlDocument(); manifest.Load(file); var manifestNsmgr = CreateDefaultXmlNsmgr(manifest); var srcAssemblyId = manifest.SelectSingleNode("/asmv1:assembly/asmv1:assemblyIdentity", manifestNsmgr); var assemblyIdentity = doc.CreateNode(XmlNodeType.Element, "assemblyIdentity", ASMv2_NS); foreach (var attrName in new[] { "name", "version", "language", "processorArchitecture", "publicKeyToken", "type" }) { SetOrReplaceAttribute(assemblyIdentity, attrName, null, srcAssemblyId.Attributes[attrName].Value); } dependentAssembly.AppendChild(assemblyIdentity); var hash = CreateHashNode(file, nsmgr, doc); dependentAssembly.AppendChild(hash); dependency.AppendChild(dependentAssembly); dependencyList.InsertAfter(dependency, lastPrerequisite); } else { var fileNode = doc.CreateNode(XmlNodeType.Element, "file", ASMv2_NS); SetOrReplaceAttribute(fileNode, "name", null, file.Replace('/', '\\')); SetOrReplaceAttribute(fileNode, "size", null, string.Format(CultureInfo.InvariantCulture, "{0}", new FileInfo(file).Length)); var hash = doc.CreateNode(XmlNodeType.Element, "hash", ASMv2_NS); var transforms = doc.CreateNode(XmlNodeType.Element, "Transforms", XMLDSIG_NS); var transform = doc.CreateNode(XmlNodeType.Element, "Transform", XMLDSIG_NS); SetOrReplaceAttribute(transform, "Algorithm", null, XMLDSIG_IDENTITY); transforms.AppendChild(transform); hash.AppendChild(transforms); var digestMethod = doc.CreateNode(XmlNodeType.Element, "DigestMethod", XMLDSIG_NS); hash.AppendChild(digestMethod); var digestValue = doc.CreateNode(XmlNodeType.Element, "DigestValue", XMLDSIG_NS); hash.AppendChild(digestValue); UpdateSha1(hash, file, nsmgr); fileNode.AppendChild(hash); dependencyList.InsertAfter(fileNode, lastPrerequisite); deployTarget += ".deploy"; } Directory.CreateDirectory(Path.GetDirectoryName(deployTarget)); File.Copy(file, deployTarget, true); }
private static void EnforceAppServer(EnvConfig envConfig) { if (envConfig.AppServer == null || string.IsNullOrEmpty(envConfig.AppServer.Uri)) return; LogTitle("Enforcing app server"); foreach (var configPath in GetAppconfigFilenames(envConfig.BinaryTarget) .Concat(GetAppconfigFilenames(envConfig.TestsTarget))) { var doc = new XmlDocument(); doc.Load(configPath); var endpoints = doc.SelectNodes("/configuration/system.serviceModel/client/endpoint"); foreach (XmlElement endpoint in endpoints) { endpoint.Attributes["address"].Value = envConfig.AppServer.Uri; LogAction("set endpoint address in {0}", configPath); } var serviceUris = doc.SelectNodes("/configuration/appSettings/add[@key='serviceUri']"); foreach (XmlElement serviceUri in serviceUris) { serviceUri.Attributes["value"].Value = envConfig.AppServer.Uri; LogAction("set serviceUri address in {0}", configPath); } doc.Save(configPath); } }
private static System.Reflection.Assembly FillAppId(EnvConfig envConfig, XmlNode assemblyIdentity, AppId appId) { var client = System.Reflection.Assembly.ReflectionOnlyLoadFrom(envConfig.ClientExe); FillClickOnceAssemblyId(client, assemblyIdentity); // this seems to be a constant SetOrReplaceAttribute(assemblyIdentity, "type", null, "win32"); // asmv1 wants a filename, not an assembly name SetOrReplaceAttribute(assemblyIdentity, "name", null, Path.GetFileName(envConfig.ClientExe)); SetOrReplaceAttribute(assemblyIdentity, "version", null, appId.version); SetOrReplaceAttribute(assemblyIdentity, "publicKeyToken", null, appId.publicKeyToken); return client; }
private static void CreateClickOnceManifest(EnvConfig envConfig, AppId appId) { var doc = LoadXmlFromResource("PrepareEnv.Templates.ClickOnce.manifest.xml"); var nsmgr = CreateDefaultXmlNsmgr(doc); // fixup primary asmv1:assemblyIdentity var assemblyIdentity1 = doc.SelectSingleNode("/asmv1:assembly/asmv1:assemblyIdentity", nsmgr); var clientName = FillAppId(envConfig, assemblyIdentity1, appId); // fixup entryPoint's assemblyIdentity var entryPointId = doc.SelectSingleNode("/asmv1:assembly/asmv2:entryPoint/asmv2:assemblyIdentity", nsmgr); FillClickOnceAssemblyId(clientName, entryPointId); // fixup Icon var description = doc.SelectSingleNode("/asmv1:assembly/asmv1:description", nsmgr); description.Attributes["asmv2:iconFile"].Value = Path.GetFileName(envConfig.ClientExe); // set the startup paramters var entryPointCli = doc.SelectSingleNode("/asmv1:assembly/asmv2:entryPoint/asmv2:commandLine", nsmgr); entryPointCli.Attributes["file"].Value = Path.GetFileName(envConfig.ClientExe); entryPointCli.Attributes["parameters"].Value = envConfig.ClientParameters; // insert deployed files var dependencyList = doc.SelectSingleNode("/asmv1:assembly", nsmgr); var lastPrerequisite = doc.SelectNodes("/asmv1:assembly/asmv2:dependency", nsmgr).OfType<XmlNode>().Last(); foreach (var baseName in new[] { "Common", "Client" }) { foreach (var pattern in new[] { "*.dll", "*.exe", "*.exe.config" }) { foreach (var file in Directory.EnumerateFiles(baseName, pattern, SearchOption.AllDirectories)) { InsertClickOnceDependency(envConfig, dependencyList, lastPrerequisite, file, nsmgr); } } } // Add Client EXE and config InsertClickOnceDependency(envConfig, dependencyList, lastPrerequisite, envConfig.ClientExe, nsmgr); InsertClickOnceDependency(envConfig, dependencyList, lastPrerequisite, envConfig.ClientExe + ".config", nsmgr); InsertClickOnceDependency(envConfig, dependencyList, lastPrerequisite, Path.Combine("Configs", Path.GetFileNameWithoutExtension(envConfig.ClientExe) + ".xml"), nsmgr); // save to template doc.Save(GetManifestTemplateName(envConfig)); }
private static void PrepareEnvConfig(EnvConfig envConfig, string envConfigDir) { if (envConfig.AppServer != null) { envConfig.AppServer.Type = ExpandEnvVars(envConfig.AppServer.Type); envConfig.AppServer.Uri = ExpandEnvVars(envConfig.AppServer.Uri); } envConfig.BinarySource = PrepareConfigPath(envConfig.BinarySource); envConfig.GeneratedSource = PrepareConfigPath(envConfig.GeneratedSource); envConfig.BinaryTarget = PrepareConfigPath(envConfig.BinaryTarget); envConfig.TestsTarget = PrepareConfigPath(envConfig.TestsTarget); envConfig.ClientExe = ExpandEnvVars(envConfig.ClientExe ?? "Zetbox.WPF.exe"); envConfig.ClientParameters = PrepareConfigPath(envConfig.ClientParameters); if (envConfig.ClickOnce != null) { envConfig.ClickOnce.Publisher = ExpandEnvVars(envConfig.ClickOnce.Publisher); envConfig.ClickOnce.SuiteName = ExpandEnvVars(envConfig.ClickOnce.SuiteName); // provide a sensible default envConfig.ClickOnce.Product = ExpandEnvVars(envConfig.ClickOnce.Product) ?? string.Format("{0}'s zetbox client", envConfig.ClickOnce.Publisher); envConfig.ClickOnce.SupportUrl = ExpandEnvVars(envConfig.ClickOnce.SupportUrl); envConfig.ClickOnce.ErrorReportUrl = ExpandEnvVars(envConfig.ClickOnce.ErrorReportUrl); // if no URL is specified, the ClickOnce installer will use the .application location automatically envConfig.ClickOnce.UpdateUrl = ExpandEnvVars(envConfig.ClickOnce.UpdateUrl); envConfig.ClickOnce.KeyFile = PrepareConfigPath(envConfig.ClickOnce.KeyFile); envConfig.ClickOnce.DeploymentVersion = ExpandEnvVars(envConfig.ClickOnce.DeploymentVersion); } if (string.IsNullOrEmpty(envConfig.ConfigSource)) { envConfig.ConfigSource = envConfigDir; } else { envConfig.ConfigSource = PrepareConfigPath(envConfig.ConfigSource); } if (envConfig.DatabaseSource != null) { envConfig.DatabaseSource.Provider = ExpandEnvVars(envConfig.DatabaseSource.Provider); envConfig.DatabaseSource.Schema = ExpandEnvVars(envConfig.DatabaseSource.Schema); envConfig.DatabaseSource.Value = ExpandEnvVars(envConfig.DatabaseSource.Value); } if (envConfig.DatabaseTarget != null) { envConfig.DatabaseTarget.Provider = ExpandEnvVars(envConfig.DatabaseTarget.Provider); envConfig.DatabaseTarget.Schema = ExpandEnvVars(envConfig.DatabaseTarget.Schema); envConfig.DatabaseTarget.Value = ExpandEnvVars(envConfig.DatabaseTarget.Value); } }
private static void InstallTestsGeneratedBinaries(EnvConfig envConfig) { InstallGeneratedBinaries(envConfig.GeneratedSource, envConfig.TestsTarget, CopyMode.Flat); }
/// <summary> /// Copy from envConfig.BinarySource to envConfig.TestsTarget /// </summary> /// <param name="envConfig"></param> private static void InstallTestsBinaries(EnvConfig envConfig) { LogTitle("Installing Tests Binaries"); // if source is empty or source and target are the same, binaries do not have to be copied if (!string.IsNullOrEmpty(envConfig.TestsTarget) && !string.IsNullOrEmpty(envConfig.BinarySource) && envConfig.BinarySource != envConfig.TestsTarget) { var sourcePaths = ExpandPath(envConfig.BinarySource); var isWildcard = sourcePaths.Count() > 1; foreach (var source in sourcePaths) { LogAction("copying Binaries from " + source); if (isWildcard && !Directory.Exists(source)) continue; CopyFolder(source, envConfig.TestsTarget, null, "*.exe", CopyMode.Flat); // does not handle sattelite assemblies CopyFolder(source, envConfig.TestsTarget, null, "*.exe.config", CopyMode.Flat); CopyFolder(source, envConfig.TestsTarget, null, "*.dll", CopyMode.Flat); // does not handle sattelite assemblies CopyFolder(source, envConfig.TestsTarget, null, "*.dll.config", CopyMode.Flat); CopyFolder(source, envConfig.TestsTarget, null, "*.pdb", CopyMode.Flat); CopyFolder(source, envConfig.TestsTarget, null, "*.mdb", CopyMode.Flat); } // Replace fallback binaries when generated ones are available foreach (var generatedSource in new[] { Path.Combine(envConfig.BinarySource, "Common", "Generated"), Path.Combine(envConfig.BinarySource, "Client", "Generated"), Path.Combine(envConfig.BinarySource, "Server", "Generated") }) { var generatedSourcePaths = ExpandPath(generatedSource); foreach (var path in generatedSourcePaths) { LogDetail("looking at [{0}]", path); if (Directory.Exists(path)) { LogDetail("found"); CopyTopFiles(path, envConfig.TestsTarget, null); } else { LogDetail("not found"); } } } } }
private static void InstallGeneratedBinaries(EnvConfig envConfig) { InstallGeneratedBinaries(envConfig.GeneratedSource, envConfig.BinaryTarget, CopyMode.RestoreHierarchie); }
private static void SignMage(EnvConfig envConfig, string templateName, string outputName) { Program.LogAction("signing with mage: [{0}]", outputName); var pw = _passphrase == null ? string.Empty : string.Format("-Password {0}", _passphrase); var args = string.Format("-Sign {0} -ToFile {1} -Certfile {2} {3}", templateName, outputName, envConfig.ClickOnce.KeyFile, pw); var proc = Process.Start(LocateMage(), args); proc.WaitForExit(); if (proc.ExitCode != 0) { throw new InvalidOperationException(string.Format("mage.exe complained about {0}", args)); } }
private static void SignXmlSec1(EnvConfig envConfig, string templateName, string outputName, AppId appId) { Program.LogAction("signing with xmlsec1: [{0}] => [{1}]", templateName, outputName); // load manifest input xml var docTemplate = new XmlDocument(); docTemplate.Load(templateName); var nsmgr = CreateDefaultXmlNsmgr(docTemplate); // insert publisher identity var pfx = UnlockPfx(File.ReadAllBytes(envConfig.ClickOnce.KeyFile)); var cert = pfx.Certificates.Cast<X509Certificate>().Single(); var publisherName = cert.SubjectName; // as described on http://msdn.microsoft.com/en-us/library/dd996956.aspx var issuerKeyHash = FormatKey(_sha1.ComputeHash(cert.PublicKey)); var publisherIdentity = docTemplate.CreateElement("publisherIdentity", ASMv2_NS); SetOrReplaceAttribute(publisherIdentity, "name", null, publisherName); SetOrReplaceAttribute(publisherIdentity, "issuerKeyHash", null, issuerKeyHash); docTemplate.ChildNodes.OfType<XmlElement>().Last().AppendChild(publisherIdentity); var fusionTemplateName = templateName + ".fusion"; docTemplate.Save(fusionTemplateName); // // Calculate ManifestInformation Hash // ================================== // The Fusion XML engine is always preserving whitespace, therefore we need to // use a specially configured XmlDocument to normalize and sign the Manifest. // byte[] hash; { var fusionDoc = new XmlDocument(); fusionDoc.PreserveWhitespace = true; fusionDoc.Load(fusionTemplateName); var transform = new XmlDsigExcC14NTransform(); transform.LoadInput(fusionDoc); hash = _sha1.ComputeHash((MemoryStream)transform.GetOutput()); } // Load SignatureBlock into DOM var signatureTemplate = LoadXmlFromResource("PrepareEnv.Templates.SignatureBlock.xml"); foreach (XmlNode sigNode in signatureTemplate.DocumentElement.ChildNodes) { var newChild = docTemplate.ImportNode(sigNode, deep: true); docTemplate.DocumentElement.AppendChild(newChild); foreach (XmlNode assemblyId in newChild.SelectNodes("//as:assemblyIdentity", nsmgr)) { // authenticode assemblyIdentity looks like asmv1:assemblyIdentity FillAppId(envConfig, assemblyId, appId); } } // Set ManifestInformation Hash var manifestInfo = docTemplate.SelectSingleNode("//as:ManifestInformation", nsmgr); SetOrReplaceAttribute(manifestInfo, "Hash", null, FormatKey(hash)); // Set AuthenticodePublisher's SubjectName var subjectName = docTemplate.SelectSingleNode("//as:AuthenticodePublisher/as:X509SubjectName", nsmgr); subjectName.InnerText = publisherName; // Sign everything Program.LogDetail("saving to xmlsec1 template: [{0}]", templateName + ".xmlsec1"); docTemplate.Save(templateName + ".xmlsec1"); var pw = _passphrase == null ? string.Empty : string.Format("--pwd \"{0}\"", _passphrase); // resign manifest RelData var relDataArgs = string.Format("--sign {0} {1} {2} --node-xpath \"//*[local-name()='RelData']\" --enabled-key-data rsa,x509 --output \"{3}.reldata\" \"{4}.xmlsec1\"", pw, envConfig.ClickOnce.KeyFile.EndsWith("pfx") ? "--pkcs12" : "--privkey-pem", envConfig.ClickOnce.KeyFile, outputName, templateName); Program.LogDetail("signing reldata to [{0}.reldata]", outputName); var proc = Process.Start(new ProcessStartInfo("xmlsec1", relDataArgs) { UseShellExecute = false }); proc.WaitForExit(); if (proc.ExitCode != 0) { throw new InvalidOperationException(string.Format("xmlsec1 complained about {0}", relDataArgs)); } // resign complete manifest var finalArgs = string.Format("--sign {0} {1} {2} --enabled-key-data rsa,x509 --output \"{3}\" \"{3}.reldata\"", pw, envConfig.ClickOnce.KeyFile.EndsWith("pfx") ? "--pkcs12" : "--privkey-pem", envConfig.ClickOnce.KeyFile, outputName); Program.LogDetail("signing final to : [{0}]", outputName); proc = Process.Start(new ProcessStartInfo("xmlsec1", finalArgs) { UseShellExecute = false }); proc.WaitForExit(); if (proc.ExitCode != 0) { throw new InvalidOperationException(string.Format("xmlsec1 complained about {0}", finalArgs)); } }
private static string GetAppName(EnvConfig envConfig) { return Path.Combine(GetClickOnceOutputPath(envConfig), Path.GetFileNameWithoutExtension(envConfig.ClientExe) + ".application"); }
private static void CreateClickOnceAppplication(EnvConfig envConfig, AppId appId) { var doc = LoadXmlFromResource("PrepareEnv.Templates.ClickOnce.application.xml"); var nsmgr = CreateDefaultXmlNsmgr(doc); // primary asmv1:assemblyIdentity var assemblyIdentity1 = doc.SelectSingleNode("/asmv1:assembly/asmv1:assemblyIdentity", nsmgr); // name of the application manifest SetOrReplaceAttribute(assemblyIdentity1, "name", null, Path.GetFileName(GetAppName(envConfig))); // deployment version SetOrReplaceAttribute(assemblyIdentity1, "version", null, envConfig.ClickOnce.DeploymentVersion); // key token of the signing key SetOrReplaceAttribute(assemblyIdentity1, "publicKeyToken", null, FormatKey(PublicKeyTokenFromPfx(envConfig.ClickOnce.KeyFile))); // from the underlying .exe SetOrReplaceAttribute(assemblyIdentity1, "language", null, "neutral"); SetOrReplaceAttribute(assemblyIdentity1, "processorArchitecture", null, "msil"); // application description var description = doc.SelectSingleNode("/asmv1:assembly/asmv1:description", nsmgr); SetOrReplaceAttribute(description, "publisher", ASMv2_NS, envConfig.ClickOnce.Publisher); SetOrReplaceAttribute(description, "product", ASMv2_NS, envConfig.ClickOnce.Product); if (!string.IsNullOrEmpty(envConfig.ClickOnce.SuiteName)) { SetOrReplaceAttribute(description, "suiteName", null, envConfig.ClickOnce.SuiteName); } if (!string.IsNullOrEmpty(envConfig.ClickOnce.SupportUrl)) { SetOrReplaceAttribute(description, "supportUrl", null, envConfig.ClickOnce.SupportUrl); } if (!string.IsNullOrEmpty(envConfig.ClickOnce.ErrorReportUrl)) { SetOrReplaceAttribute(description, "errorReportUrl", null, envConfig.ClickOnce.ErrorReportUrl); } // deployment options var deploymentProvider = doc.SelectSingleNode("/asmv1:assembly/asmv2:deployment/asmv2:deploymentProvider", nsmgr); SetOrReplaceAttribute(deploymentProvider, "codebase", null, envConfig.ClickOnce.UpdateUrl); // insert manifest file var dependencyList = doc.SelectSingleNode("/asmv1:assembly", nsmgr); var lastPrerequisite = doc.SelectNodes("/asmv1:assembly/co.v2:compatibleFrameworks", nsmgr).OfType<XmlNode>().Last(); InsertClickOnceDependency(envConfig, dependencyList, lastPrerequisite, GetManifestName(envConfig), nsmgr); // save to template doc.Save(GetAppTemplateName(envConfig)); }
/// <summary> /// forces all configuration's connection string to be envConfig.DatabaseTarget /// </summary> /// <param name="envConfig"></param> private static void EnforceConnectionString(EnvConfig envConfig) { if (envConfig.DatabaseTarget == null) return; LogTitle("Enforcing connection string"); foreach (var configPath in GetConfigFilenames(envConfig.BinaryTarget) .Concat(GetConfigFilenames(envConfig.TestsTarget))) { var doc = new XmlDocument(); doc.Load(configPath); // create prefix<->namespace mappings (if any) var nsMgr = new XmlNamespaceManager(doc.NameTable); nsMgr.AddNamespace("k", "http://dasz.at/Zetbox/"); // check whether this is a ZetboxConfig var configSet = doc.SelectNodes("/k:ZetboxConfig", nsMgr); if (configSet.Count == 0) continue; // nope, ignore! var serverNode = doc.SelectSingleNode("/k:ZetboxConfig/k:Server[@StartServer='true']", nsMgr); if (serverNode == null) continue; // no startable server, ignore! // Select a database called "Zetbox" var databaseNode = doc.SelectSingleNode("/k:ZetboxConfig/k:Server[@StartServer=true]/k:ConnectionStrings/k:Database[@Name=Zetbox]", nsMgr); if (databaseNode == null) { databaseNode = doc.CreateElement("Database", "http://dasz.at/Zetbox/"); var connectionStringsNode = doc.SelectSingleNode("/k:ZetboxConfig/k:Server[@StartServer=true]/k:ConnectionStrings", nsMgr); if (connectionStringsNode == null) { connectionStringsNode = doc.CreateElement("ConnectionStrings", "http://dasz.at/Zetbox/"); serverNode.PrependChild(connectionStringsNode); } connectionStringsNode.PrependChild(databaseNode); } EnsureAttribute(doc, databaseNode, "Name"); databaseNode.Attributes["Name"].Value = "Zetbox"; EnsureAttribute(doc, databaseNode, "Schema"); databaseNode.Attributes["Schema"].Value = envConfig.DatabaseTarget.Schema; EnsureAttribute(doc, databaseNode, "Provider"); databaseNode.Attributes["Provider"].Value = envConfig.DatabaseTarget.Provider; databaseNode.InnerText = envConfig.DatabaseTarget.Value; LogAction("set connection string in {0}", configPath); doc.Save(configPath); } }
private static void DeployDatabaseTemplate(EnvConfig envConfig) { if (envConfig.DatabaseSource == null || envConfig.DatabaseTarget == null) return; LogTitle("Deploying Database Template"); // Delete Target using (var schemaProvider = SchemaProvider.SchemaProviderFactory.CreateInstance(envConfig.DatabaseTarget.Schema)) { LogAction("drop target database contents"); schemaProvider.Open(envConfig.DatabaseTarget.Value); schemaProvider.DropAllObjects(); } if (envConfig.DatabaseSource.Schema != "EMPTY" && !string.IsNullOrEmpty(envConfig.DatabaseSource.Value) && !string.IsNullOrEmpty(envConfig.DatabaseTarget.Value)) { // Copy database using (var schemaProvider = SchemaProvider.SchemaProviderFactory.CreateInstance(envConfig.DatabaseTarget.Schema)) { LogAction("copying database"); schemaProvider.Copy(envConfig.DatabaseSource.Value, envConfig.DatabaseTarget.Value); } } }
// will be copied by InsertClickOnceDependency private static string GetManifestName(EnvConfig envConfig) { return envConfig.ClientExe + ".manifest"; }
private static string GetManifestTemplateName(EnvConfig envConfig) { return GetManifestName(envConfig) + ".tmpl"; }
// template should not go to ClickOnceOutputPath private static string GetAppTemplateName(EnvConfig envConfig) { return Path.GetFileNameWithoutExtension(envConfig.ClientExe) + ".application.tmpl"; }
/// <summary> /// Copy from envConfig.BinarySource to envConfig.BinaryTarget; fetch files from Modules\ and Data\ /// </summary> /// <param name="envConfig"></param> private static void InstallBinaries(EnvConfig envConfig) { LogTitle("Installing Binaries"); // if source is empty or source and target are the same, binaries do not have to be copied if (!string.IsNullOrEmpty(envConfig.BinarySource) && !string.IsNullOrEmpty(envConfig.BinaryTarget) && envConfig.BinarySource != envConfig.BinaryTarget) { var sourcePaths = ExpandPath(envConfig.BinarySource); var isWildcard = sourcePaths.Count() > 1; var copiedCommonFiles = new List<string>(); var additionalCopiedFiles = new List<string>(); foreach (var source in sourcePaths) { if (isWildcard && !Directory.Exists(source)) continue; LogAction("copying common Binaries from " + source); if (Directory.Exists(Path.Combine(source, "Common"))) { var copiedPaths = CopyFolder(Path.Combine(source, "Common"), Path.Combine(envConfig.BinaryTarget, "Common")); copiedCommonFiles.AddRange(copiedPaths.Select(s => Path.GetFileName(s))); } } foreach (var source in sourcePaths) { if (isWildcard && !Directory.Exists(source)) continue; LogAction("copying Binaries from " + source); if (Directory.Exists(Path.Combine(source, "Server"))) { var copiedPaths = CopyFolder(Path.Combine(source, "Server"), Path.Combine(envConfig.BinaryTarget, "Server"), copiedCommonFiles, null, CopyMode.RestoreHierarchie); additionalCopiedFiles.AddRange(copiedPaths.Select(s => Path.GetFileName(s))); } if (Directory.Exists(Path.Combine(source, "Client"))) { var copiedPaths = CopyFolder(Path.Combine(source, "Client"), Path.Combine(envConfig.BinaryTarget, "Client"), copiedCommonFiles, null, CopyMode.RestoreHierarchie); additionalCopiedFiles.AddRange(copiedPaths.Select(s => Path.GetFileName(s))); } if (Directory.Exists(Path.Combine(source, "Modules"))) { CopyFolder(Path.Combine(source, "Modules"), Path.Combine(envConfig.BinaryTarget, "Modules")); } if (Directory.Exists(Path.Combine(source, "Data"))) { CopyFolder(Path.Combine(source, "Data"), Path.Combine(envConfig.BinaryTarget, "Data")); } } additionalCopiedFiles.AddRange(copiedCommonFiles); LogAction("copying HttpService and associated binaries"); foreach (var source in sourcePaths) { if (isWildcard && !Directory.Exists(source)) continue; if (Directory.Exists(Path.Combine(source, "HttpService"))) { LogDetail("copying from " + source); CopyFolder(Path.Combine(source, "HttpService"), Path.Combine(envConfig.BinaryTarget, "HttpService")); } } LogDetail("copying from deployed Common"); CopyFolder(Path.Combine(envConfig.BinaryTarget, "Common"), Path.Combine(envConfig.BinaryTarget, "HttpService", "bin", "Common")); LogDetail("copying from deployed Server"); CopyFolder(Path.Combine(envConfig.BinaryTarget, "Server"), Path.Combine(envConfig.BinaryTarget, "HttpService", "bin", "Server")); LogAction("copying ASPNET and associated binaries"); foreach (var source in sourcePaths) { if (isWildcard && !Directory.Exists(source)) continue; if (Directory.Exists(Path.Combine(source, "ASPNET"))) { LogDetail("copying from " + source); CopyFolder(Path.Combine(source, "ASPNET"), Path.Combine(envConfig.BinaryTarget, "ASPNET")); } } LogDetail("copying from deployed Common"); CopyFolder(Path.Combine(envConfig.BinaryTarget, "Common"), Path.Combine(envConfig.BinaryTarget, "ASPNET", "bin", "Common")); LogDetail("copying from deployed Server"); CopyFolder(Path.Combine(envConfig.BinaryTarget, "Server"), Path.Combine(envConfig.BinaryTarget, "ASPNET", "bin", "Server")); LogDetail("copying from deployed Client"); CopyFolder(Path.Combine(envConfig.BinaryTarget, "Client"), Path.Combine(envConfig.BinaryTarget, "ASPNET", "bin", "Client")); foreach (var source in sourcePaths) { LogAction("copying executables from " + source); CopyTopFiles(source, envConfig.BinaryTarget, additionalCopiedFiles); LogAction("copying Bootstrapper from " + source); if (isWildcard && !Directory.Exists(source)) continue; var bootstrapperSource = Path.Combine(source, "Bootstrapper"); if (Directory.Exists(bootstrapperSource)) { // Bootstrapper has to be available in the web root CopyFolder(bootstrapperSource, Path.Combine(envConfig.BinaryTarget, "HttpService", "Bootstrapper")); } } // The following will be needed when deploying if (Directory.Exists("Modules")) { LogAction("copying local Modules"); CopyFolder("Modules", Path.Combine(envConfig.BinaryTarget, "Modules")); } if (Directory.Exists("Data")) { LogAction("copying local Data"); CopyFolder("Data", Path.Combine(envConfig.BinaryTarget, "Data")); } } }
private static string GetClickOnceOutputPath(EnvConfig envConfig) { return "ClickOnceClient"; }
/// <summary> /// Copy from envConfig.BinarySource to envConfig.BinaryTarget; fetch files from Modules\ and Data\ /// </summary> /// <param name="envConfig"></param> private static void InstallBinaries(EnvConfig envConfig) { LogTitle("Installing Binaries"); // if source is empty or source and target are the same, binaries do not have to be copied if (!string.IsNullOrEmpty(envConfig.BinarySource) && !string.IsNullOrEmpty(envConfig.BinaryTarget) && envConfig.BinarySource != envConfig.BinaryTarget) { var sourcePaths = ExpandPath(envConfig.BinarySource); var isWildcard = sourcePaths.Count() > 1; foreach (var source in sourcePaths) { LogAction("copying Binaries from " + source); if (isWildcard && !Directory.Exists(source)) continue; CopyFolder(source, envConfig.BinaryTarget); var bootstrapperSource = Path.Combine(source, "Bootstrapper"); if (Directory.Exists(bootstrapperSource)) { // Bootstrapper has to be available in the web root CopyFolder(bootstrapperSource, PathX.Combine(envConfig.BinaryTarget, "HttpService", "Bootstrapper")); } } var moduleTarget = Path.Combine(envConfig.BinaryTarget, "Modules"); if (Directory.Exists("Modules")) { LogAction("copying Modules"); CopyFolder("Modules", moduleTarget); } var dataTarget = Path.Combine(envConfig.BinaryTarget, "Data"); if (Directory.Exists("Data")) { LogAction("copying Data"); CopyFolder("Data", dataTarget); } ReplaceNpgsql(envConfig.BinarySource, envConfig.BinaryTarget); } }