From eed5a3a97301f10c727749ece1ee7ff175312089 Mon Sep 17 00:00:00 2001 From: Morten Holt Date: Fri, 12 Apr 2024 09:02:19 +0200 Subject: [PATCH 1/7] Compare version between local and registered assembly. If the local is newer than the registered, do not care about the hash value. --- src/Delegate.Daxif/AssemblyInfo.fs | 8 ++--- src/Delegate.Daxif/Common/Utility.fs | 3 ++ src/Delegate.Daxif/Daxif.fs | 5 +++ src/Delegate.Daxif/Modules/Plugins/Compare.fs | 13 ++++++-- src/Delegate.Daxif/Modules/Plugins/Domain.fs | 4 +++ src/Delegate.Daxif/Modules/Plugins/Main.fs | 4 +++ .../Modules/Plugins/MainHelper.fs | 33 +++++++++++-------- .../Modules/Plugins/PluginDetection.fs | 3 +- src/Delegate.Daxif/Modules/Plugins/Query.fs | 4 +-- .../Modules/Plugins/Retrieval.fs | 5 +++ src/Delegate.Daxif/RELEASE_NOTES.md | 3 ++ 11 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/Delegate.Daxif/AssemblyInfo.fs b/src/Delegate.Daxif/AssemblyInfo.fs index 66606fe..7aae094 100644 --- a/src/Delegate.Daxif/AssemblyInfo.fs +++ b/src/Delegate.Daxif/AssemblyInfo.fs @@ -7,8 +7,8 @@ open System.Reflection [] [] [] -[] -[] +[] +[] do () module internal AssemblyVersionInformation = @@ -17,5 +17,5 @@ module internal AssemblyVersionInformation = let [] AssemblyDescription = "Delegate Automated xRM Installation Framework" let [] AssemblyCompany = "Delegate" let [] AssemblyCopyright = "Copyright (c) Delegate A/S 2017" - let [] AssemblyVersion = "5.5.1" - let [] AssemblyFileVersion = "5.5.1" + let [] AssemblyVersion = "5.6.0" + let [] AssemblyFileVersion = "5.6.0" diff --git a/src/Delegate.Daxif/Common/Utility.fs b/src/Delegate.Daxif/Common/Utility.fs index 98b4d0b..0455699 100644 --- a/src/Delegate.Daxif/Common/Utility.fs +++ b/src/Delegate.Daxif/Common/Utility.fs @@ -225,6 +225,9 @@ let parseVersion (str:string): Version = let getIdx idx = Array.tryItem idx vArr ?>> parseInt ?| 0 (getIdx 0, getIdx 1, getIdx 2, getIdx 3) +let versionToString (version:Version): string = + let (a,b,c,d) = version + sprintf "%d.%d.%d.%d" a b c d let getIntGroup def (m:Match) (idx:int) = parseInt m.Groups.[idx].Value ?| def let getMinVersion = getIntGroup 0 diff --git a/src/Delegate.Daxif/Daxif.fs b/src/Delegate.Daxif/Daxif.fs index ba9a324..ed63105 100644 --- a/src/Delegate.Daxif/Daxif.fs +++ b/src/Delegate.Daxif/Daxif.fs @@ -53,6 +53,11 @@ type AsyncJobState = | Failed = 31 | Canceled = 32 +type AssemblyOperation = + | Unchanged + | Create + | Update + type Version = int * int * int * int type VersionCriteria = Version option * Version option diff --git a/src/Delegate.Daxif/Modules/Plugins/Compare.fs b/src/Delegate.Daxif/Modules/Plugins/Compare.fs index beb44ff..a2ad83c 100644 --- a/src/Delegate.Daxif/Modules/Plugins/Compare.fs +++ b/src/Delegate.Daxif/Modules/Plugins/Compare.fs @@ -2,6 +2,7 @@ open System open Microsoft.Xrm.Sdk +open DG.Daxif open DG.Daxif.Common open DG.Daxif.Common.Utility @@ -135,9 +136,17 @@ let image (img: Image) (x: Entity) = /// Compares a Custom API Response Property from CRM with one in source code // TODO - /// Compares an assembly from CRM with the one containing the source code +/// Returns true if the assembly in CRM is newer or the hash matches the one in the source code let assembly (local: AssemlyLocal) (registered: AssemblyRegistration option) = registered - ?|> fun y -> y.hash = local.hash + ?|> fun y -> + let log = ConsoleLogger.Global + + let localIsOlderOrEqual = y.version .>= local.version + let hashMatch = y.hash = local.hash + log.Verbose "Comparing assembly version. Registered >= local? %b" localIsOlderOrEqual + log.Verbose "Comparing assembly hash. Hash matches? %b" hashMatch + log.Verbose "Assembly should be updated? %b" ((localIsOlderOrEqual || hashMatch) |> not) + localIsOlderOrEqual || hashMatch ?| false \ No newline at end of file diff --git a/src/Delegate.Daxif/Modules/Plugins/Domain.fs b/src/Delegate.Daxif/Modules/Plugins/Domain.fs index b0555ce..c261790 100644 --- a/src/Delegate.Daxif/Modules/Plugins/Domain.fs +++ b/src/Delegate.Daxif/Modules/Plugins/Domain.fs @@ -4,6 +4,7 @@ open System open System.Reflection open Microsoft.Xrm.Sdk open DG.Daxif +open DG.Daxif.Common (** Enum for plugin configurations **) type ExecutionMode = @@ -144,6 +145,7 @@ type AssemlyLocal = dllName: String dllPath: String hash: String + version: Version isolationMode: AssemblyIsolationMode plugins: Plugin seq customAPIs: CustomAPI seq @@ -152,9 +154,11 @@ type AssemlyLocal = type AssemblyRegistration = { id: Guid hash: String + version: Version } with static member fromEntity (e:Entity) = { id = e.Id hash = e.GetAttributeValue("sourcehash") + version = e.GetAttributeValue("version") |> Utility.parseVersion } \ No newline at end of file diff --git a/src/Delegate.Daxif/Modules/Plugins/Main.fs b/src/Delegate.Daxif/Modules/Plugins/Main.fs index 9805aab..0727b9b 100644 --- a/src/Delegate.Daxif/Modules/Plugins/Main.fs +++ b/src/Delegate.Daxif/Modules/Plugins/Main.fs @@ -25,6 +25,10 @@ let syncSolution proxyGen projectPath dllPath solutionName isolationMode ignoreO log.Info "***** Dry run *****" let regTypes, regSteps, regImages, regCustomApis, regReqParams, regRespParams = pluginsReg let localTypes, localSteps, localImages, localCustomApiTypes, localCustomApis, localReqParams, localRespParams = pluginsLocal + match MainHelper.determineOperation asmReg asmLocal with + | Unchanged, _ -> log.Info "No changes detected to assembly" + | Create, _ -> log.Info "Would create new assembly" + | Update, _ -> log.Info "Would update assembly" printMergePartition "Types" localTypes regTypes Compare.pluginType log printMergePartition "Steps" localSteps regSteps Compare.step log printMergePartition "Images" localImages regImages Compare.image log diff --git a/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs b/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs index d6b5c36..47b05ee 100644 --- a/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs +++ b/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs @@ -4,6 +4,7 @@ open System open Microsoft.Xrm.Sdk open Microsoft.Xrm.Sdk.Messages +open DG.Daxif open DG.Daxif.Common open DG.Daxif.Common.Utility @@ -61,21 +62,27 @@ let localToMaps (plugins: Plugin seq) (customAPIs: CustomAPI seq) = mergedTypeMap, stepMap, imageMap, customApiTypeMap, customApiMap, reqParamMap, respPropMap +/// Determine which operation we want to perform on the assembly +let determineOperation (asmReg: AssemblyRegistration option) (asmLocal) : AssemblyOperation * Guid = + match asmReg with + | Some asm when Compare.assembly asmLocal (Some asm) -> Unchanged, asm.id + | Some asm -> Update, asm.id + | None -> Create, Guid.Empty + /// Update or create assembly let ensureAssembly proxy solutionName asmLocal maybeAsm = - match Compare.assembly asmLocal maybeAsm with - | true -> maybeAsm.Value.id - | false -> - let asmEntity = EntitySetup.createAssembly asmLocal.dllName asmLocal.dllPath asmLocal.assembly asmLocal.hash asmLocal.isolationMode - - match maybeAsm with - | Some asmReg -> - asmEntity.Id <- asmReg.id + match determineOperation maybeAsm asmLocal with + | Unchanged, id -> + log.Info "No changes to assembly %s detected" asmLocal.dllName + id + | Update, id -> + let asmEntity = EntitySetup.createAssembly asmLocal.dllName asmLocal.dllPath asmLocal.assembly asmLocal.hash asmLocal.isolationMode + asmEntity.Id <- id CrmDataHelper.getResponse proxy (makeUpdateReq asmEntity) |> ignore log.Info "Updating %s: %s" asmEntity.LogicalName asmLocal.dllName - asmReg.id - - | None -> + id + | Create, _ -> + let asmEntity = EntitySetup.createAssembly asmLocal.dllName asmLocal.dllPath asmLocal.assembly asmLocal.hash asmLocal.isolationMode log.Info "Creating %s: %s" asmEntity.LogicalName asmLocal.dllName CrmDataHelper.getResponseWithParams proxy (makeCreateReq asmEntity) [ "SolutionUniqueName", solutionName ] |> fun r -> r.id @@ -148,9 +155,9 @@ let create proxy solutionName prefix imgDiff stepDiff apiDiff apiReqDiff apiResp /// Load a local assembly and validate its plugins let loadAndValidateAssembly proxy projectPath dllPath isolationMode ignoreOutdatedAssembly = - log.Verbose "Loading local assembly and it's plugins" + log.Verbose "Loading local assembly and its plugins" let asmLocal = PluginDetection.getAssemblyContextFromDll projectPath dllPath isolationMode ignoreOutdatedAssembly - log.Verbose "Local assembly loaded" + log.Verbose "Local assembly version %s loaded" (asmLocal.version |> versionToString) log.Verbose "Validating plugins to be registered" match Validation.validatePlugins proxy asmLocal.plugins with diff --git a/src/Delegate.Daxif/Modules/Plugins/PluginDetection.fs b/src/Delegate.Daxif/Modules/Plugins/PluginDetection.fs index a250dc5..94e896f 100644 --- a/src/Delegate.Daxif/Modules/Plugins/PluginDetection.fs +++ b/src/Delegate.Daxif/Modules/Plugins/PluginDetection.fs @@ -4,7 +4,6 @@ open System open System.IO open System.Reflection open System.Xml.Linq -open DG.Daxif.Common open DG.Daxif.Common.Utility open DG.Daxif.Common.InternalUtility @@ -279,12 +278,14 @@ let getAssemblyContextFromDll projectPath dllPath isolationMode ignoreOutdatedAs |> Set.fold (fun a x -> a + x |> sha1CheckSum) String.Empty let asm = Assembly.LoadFile(dllTempPath); + let version = asm.GetName().Version |> fun y -> (y.Major, y.Minor, y.Build, y.Revision) { assembly = asm assemblyId = None dllName = dllName dllPath = dllFullPath hash = hash + version = version isolationMode = isolationMode plugins = getPluginsFromAssembly asm customAPIs = getCustomAPIsFromAssembly asm diff --git a/src/Delegate.Daxif/Modules/Plugins/Query.fs b/src/Delegate.Daxif/Modules/Plugins/Query.fs index 69e6ef1..d80f724 100644 --- a/src/Delegate.Daxif/Modules/Plugins/Query.fs +++ b/src/Delegate.Daxif/Modules/Plugins/Query.fs @@ -6,7 +6,7 @@ open Microsoft.Xrm.Sdk.Query /// Create a query to get a plugin assembly by its name let pluginAssemblyByName (name: string) = let q = QueryExpression("pluginassembly") - q.ColumnSet <- ColumnSet("pluginassemblyid", "name", "sourcehash") + q.ColumnSet <- ColumnSet("pluginassemblyid", "name", "sourcehash", "version") let f = FilterExpression() f.AddCondition(ConditionExpression("name", ConditionOperator.Equal, name)) @@ -25,7 +25,7 @@ let pluginAssemblyByName (name: string) = /// Create a query to get plugin assemblies by solution let pluginAssembliesBySolution (solutionId: Guid) = let q = QueryExpression("pluginassembly") - q.ColumnSet <- ColumnSet("pluginassemblyid", "name", "sourcehash", "isolationmode") + q.ColumnSet <- ColumnSet("pluginassemblyid", "name", "sourcehash", "isolationmode", "version") let le = LinkEntity() le.JoinOperator <- JoinOperator.Inner diff --git a/src/Delegate.Daxif/Modules/Plugins/Retrieval.fs b/src/Delegate.Daxif/Modules/Plugins/Retrieval.fs index b6f24d4..455d14b 100644 --- a/src/Delegate.Daxif/Modules/Plugins/Retrieval.fs +++ b/src/Delegate.Daxif/Modules/Plugins/Retrieval.fs @@ -4,6 +4,7 @@ open System open Microsoft.Xrm.Sdk open Microsoft.Xrm.Sdk.Messages +open DG.Daxif open DG.Daxif.Common open DG.Daxif.Common.Utility @@ -98,6 +99,10 @@ let retrieveRegisteredByAssembly proxy solutionId assemblyName = |> Seq.tryFind (fun a -> getRecordName a = assemblyName) ?|> AssemblyRegistration.fromEntity + match targetAssembly with + | Some asm -> ConsoleLogger.Global.Verbose "Registered assembly version %s found for %s" (asm.version |> versionToString) assemblyName + | None -> ConsoleLogger.Global.Verbose "No registered assembly found matching %s" assemblyName + let maps = match targetAssembly with | None -> Map.empty, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty diff --git a/src/Delegate.Daxif/RELEASE_NOTES.md b/src/Delegate.Daxif/RELEASE_NOTES.md index e68fb5c..96c417d 100644 --- a/src/Delegate.Daxif/RELEASE_NOTES.md +++ b/src/Delegate.Daxif/RELEASE_NOTES.md @@ -1,4 +1,7 @@ # Release Notes +### 5.6.0 - April 04 2024 +* Update assembly comparison when determining if we want to update the assembly. Now also compares the version, and if the local version is larger we always sync the assembly file. + ### 5.5.1 - May 23 2023 * Fixed 'useUniqueInstance' parameter to GetCrmServiceClient() with default value: 'false'. - Developer now has to actively enable multiple instances of service client. This is due to potential authentication issues if too many simultaneous tasks are spawning connections (such as WebResourceSync functionality) (@bo-stig-christensen) From 861cace9912a9858dd53d053a5416ef384aac2ae Mon Sep 17 00:00:00 2001 From: Morten Holt Date: Fri, 12 Apr 2024 14:17:51 +0200 Subject: [PATCH 2/7] Fix: The comparison should be and instead of or, otherwise hash matching would override versoin matching. --- src/Delegate.Daxif/Modules/Plugins/Compare.fs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Delegate.Daxif/Modules/Plugins/Compare.fs b/src/Delegate.Daxif/Modules/Plugins/Compare.fs index a2ad83c..f5fd3b5 100644 --- a/src/Delegate.Daxif/Modules/Plugins/Compare.fs +++ b/src/Delegate.Daxif/Modules/Plugins/Compare.fs @@ -137,16 +137,20 @@ let image (img: Image) (x: Entity) = // TODO /// Compares an assembly from CRM with the one containing the source code -/// Returns true if the assembly in CRM is newer or the hash matches the one in the source code +/// Returns true if the assembly in CRM is newer and the hash matches the one in the source code let assembly (local: AssemlyLocal) (registered: AssemblyRegistration option) = registered ?|> fun y -> let log = ConsoleLogger.Global - let localIsOlderOrEqual = y.version .>= local.version + let environmentIsNewer = y.version .>= local.version + log.Verbose "Comparing assembly version. Registered >= local? %b" environmentIsNewer + let hashMatch = y.hash = local.hash - log.Verbose "Comparing assembly version. Registered >= local? %b" localIsOlderOrEqual log.Verbose "Comparing assembly hash. Hash matches? %b" hashMatch - log.Verbose "Assembly should be updated? %b" ((localIsOlderOrEqual || hashMatch) |> not) - localIsOlderOrEqual || hashMatch + + let isSameAssembly = environmentIsNewer && hashMatch + log.Verbose "Assembly is unchanged? %b" isSameAssembly + + isSameAssembly ?| false \ No newline at end of file From 96ca826b6244689cdd34c38281ff72817a02fe5a Mon Sep 17 00:00:00 2001 From: Morten Holt Date: Mon, 15 Apr 2024 14:23:37 +0200 Subject: [PATCH 3/7] Take a SHA hash of the actual assembly file, instead of trying to load the source files. This removes the need for access to the source and project files when syncing a plugin. --- src/Delegate.Daxif/API/Plugin.fs | 14 ++-- src/Delegate.Daxif/Modules/Plugins/Main.fs | 5 +- .../Modules/Plugins/MainHelper.fs | 10 +-- .../Modules/Plugins/PluginDetection.fs | 64 +------------------ 4 files changed, 19 insertions(+), 74 deletions(-) diff --git a/src/Delegate.Daxif/API/Plugin.fs b/src/Delegate.Daxif/API/Plugin.fs index 6ef034f..46532db 100644 --- a/src/Delegate.Daxif/API/Plugin.fs +++ b/src/Delegate.Daxif/API/Plugin.fs @@ -6,25 +6,29 @@ open DG.Daxif.Modules.Plugin module SolutionMain = DG.Daxif.Modules.Solution.Main type Plugin private () = - /// Updates plugin registrations in CRM based on the plugins found in your local assembly. /// Environment the action should be performed against. /// Path to the plugin assembly dll to be synced (usually under the project bin folder). - /// Path to the plugin assembly project (.csproj). + /// DEPRECATED: PASS AN EMPTY STRING. Path to the plugin assembly project (.csproj). /// The name of the solution to which to sync plugins /// Flag whether or not to simulate/test syncing plugins (running a 'dry run'). - defaults to: false /// Assembly Isolation Mode ('Sandbox' or 'None'). All Online environments must use 'Sandbox' - defaults to: 'Sandbox' - /// Flag whether or not to simulate/test syncing plugins (running a 'dry run'). - defaults to: false + /// DEPRECATED. Flag whether or not to simulate/test syncing plugins (running a 'dry run'). - defaults to: false /// Log Level - Error, Warning, Info, Verbose or Debug - defaults to: 'Verbose' static member Sync(env: Environment, assemblyPath: string, projectPath: string, solutionName: string, ?dryRun: bool, ?isolationMode: AssemblyIsolationMode, ?ignoreOutdatedAssembly: bool, ?logLevel: LogLevel) = + if (projectPath <> "") then + log.Warn "The 'projectPath' parameter is deprecated and will be removed in a future version. Please remove it from your code. (Pass an empty string to silence this warning)" + + if (ignoreOutdatedAssembly.IsSome) then + log.Warn "The 'ignoreOutdatedAssembly' parameter is deprecated and will be removed in a future version. Please remove it from your code." + let proxyGen = env.connect(log).GetService log.setLevelOption logLevel let dryRun = dryRun ?| false let isolationMode = isolationMode ?| AssemblyIsolationMode.Sandbox - let ignoreOutdatedAssembly = ignoreOutdatedAssembly ?| false - Main.syncSolution proxyGen projectPath assemblyPath solutionName isolationMode ignoreOutdatedAssembly dryRun |> ignore + Main.syncSolution proxyGen assemblyPath solutionName isolationMode dryRun |> ignore /// Activates or deactivates all plugin steps of a solution /// Environment the action should be performed against. diff --git a/src/Delegate.Daxif/Modules/Plugins/Main.fs b/src/Delegate.Daxif/Modules/Plugins/Main.fs index 0727b9b..eafdefa 100644 --- a/src/Delegate.Daxif/Modules/Plugins/Main.fs +++ b/src/Delegate.Daxif/Modules/Plugins/Main.fs @@ -2,7 +2,6 @@ open DG.Daxif open DG.Daxif.Modules.Plugin -open DG.Daxif.Common open DG.Daxif.Common.Utility open DG.Daxif.Common.InternalUtility @@ -10,12 +9,12 @@ open Domain /// Main plugin synchronization function -let syncSolution proxyGen projectPath dllPath solutionName isolationMode ignoreOutdatedAssembly dryRun = +let syncSolution proxyGen dllPath solutionName isolationMode dryRun = logVersion log log.Info "Action: Plugin synchronization" log.Info "Comparing plugins registered in CRM versus those found in your local code" - let asmLocal, asmReg, pluginsLocal, pluginsReg, prefix = MainHelper.analyze proxyGen projectPath dllPath solutionName isolationMode ignoreOutdatedAssembly + let asmLocal, asmReg, pluginsLocal, pluginsReg, prefix = MainHelper.analyze proxyGen dllPath solutionName isolationMode match dryRun with | false -> diff --git a/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs b/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs index 47b05ee..d70f93e 100644 --- a/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs +++ b/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs @@ -154,9 +154,9 @@ let create proxy solutionName prefix imgDiff stepDiff apiDiff apiReqDiff apiResp /// Load a local assembly and validate its plugins -let loadAndValidateAssembly proxy projectPath dllPath isolationMode ignoreOutdatedAssembly = +let loadAndValidateAssembly proxy dllPath isolationMode = log.Verbose "Loading local assembly and its plugins" - let asmLocal = PluginDetection.getAssemblyContextFromDll projectPath dllPath isolationMode ignoreOutdatedAssembly + let asmLocal = PluginDetection.getAssemblyContextFromDll dllPath isolationMode log.Verbose "Local assembly version %s loaded" (asmLocal.version |> versionToString) log.Verbose "Validating plugins to be registered" @@ -169,12 +169,12 @@ let loadAndValidateAssembly proxy projectPath dllPath isolationMode ignoreOutdat /// Analyzes local and remote registrations and returns the information about each of them -let analyze proxyGen projectPath dllPath solutionName isolationMode ignoreOutdatedAssembly = +let analyze proxyGen dllPath solutionName isolationMode = let proxy = proxyGen() - let asmLocal = loadAndValidateAssembly proxy projectPath dllPath isolationMode ignoreOutdatedAssembly + let asmLocal = loadAndValidateAssembly proxy dllPath isolationMode let solutionId = CrmDataInternal.Entities.retrieveSolutionId proxy solutionName - let id, prefix = CrmDataInternal.Entities.retrieveSolutionIdAndPrefix proxy solutionName + let _id, prefix = CrmDataInternal.Entities.retrieveSolutionIdAndPrefix proxy solutionName let asmReg, pluginsReg = Retrieval.retrieveRegisteredByAssembly proxy solutionId asmLocal.dllName let pluginsLocal = localToMaps asmLocal.plugins asmLocal.customAPIs diff --git a/src/Delegate.Daxif/Modules/Plugins/PluginDetection.fs b/src/Delegate.Daxif/Modules/Plugins/PluginDetection.fs index 94e896f..3f78b98 100644 --- a/src/Delegate.Daxif/Modules/Plugins/PluginDetection.fs +++ b/src/Delegate.Daxif/Modules/Plugins/PluginDetection.fs @@ -3,58 +3,11 @@ open System open System.IO open System.Reflection -open System.Xml.Linq open DG.Daxif.Common.Utility open DG.Daxif.Common.InternalUtility open Domain - -/// Used to retrieve a .vsproj dependencies (recursive) -let projDependencies (vsproj:string) = - let getElemName name = - XName.Get(name, "http://schemas.microsoft.com/developer/msbuild/2003") - - let getElemValue name (parent : XElement) = - let elem = parent.Element(getElemName name) - if elem = null || String.IsNullOrEmpty elem.Value then None - else Some(elem.Value) - - let getAttrValue name (elem : XElement) = - let attr = elem.Attribute(XName.Get name) - if attr = null || String.IsNullOrEmpty attr.Value then None - else Some(attr.Value) - - let fullpath path1 path2 = Path.GetFullPath(Path.Combine(path1, path2)) - - let rec projDependencies' vsproj' = seq { - let vsProjXml = XDocument.Load(uri = vsproj') - - let path = Path.GetDirectoryName(vsproj') - - let projRefs = - vsProjXml.Document.Descendants(getElemName "ProjectReference") - |> Seq.choose (fun elem -> getAttrValue "Include" elem) - |> Seq.map(fun elem -> fullpath path elem) - - let refs = - vsProjXml.Document.Descendants(getElemName "Reference") - |> Seq.choose (fun elem -> getElemValue "HintPath" elem ?|? getAttrValue "Include" elem) - |> Seq.filter (fun ref -> ref.EndsWith(".dll")) - |> Seq.map(fun elem -> fullpath path elem) - - let files = - vsProjXml.Document.Descendants(getElemName "Compile") - |> Seq.choose (fun elem -> getAttrValue "Include" elem) - |> Seq.map(fun elem -> fullpath path elem) - - for projRef in projRefs do - yield! projDependencies' projRef - yield! refs - yield! files } - - projDependencies' (Path.GetFullPath(vsproj)) - /// Transforms the received tuple from the assembly file through invocation into /// plugin, step and image records let tupleToPlugin @@ -259,27 +212,16 @@ let getCustomAPIsFromAssembly (asm: Assembly) = (getFullException(ex)) /// Analyzes an assembly based on a path to its compiled assembly and its project file -let getAssemblyContextFromDll projectPath dllPath isolationMode ignoreOutdatedAssembly = +let getAssemblyContextFromDll dllPath isolationMode = let dllFullPath = Path.GetFullPath(dllPath) let dllTempPath = Path.Combine(Path.GetTempPath(),Guid.NewGuid().ToString() + @".dll") let dllName = Path.GetFileNameWithoutExtension(dllFullPath); File.Copy(dllFullPath, dllTempPath, true) - - let asmWriteTime = File.GetLastWriteTimeUtc dllFullPath - let hash = - projDependencies projectPath - |> Set.ofSeq - |> Set.map(fun x -> - match not(ignoreOutdatedAssembly) && File.GetLastWriteTimeUtc x > asmWriteTime with - | true -> failwithf "A file in the project was updated later than compiled assembly: %s\nPlease recompile and synchronize again, or use the \"ignoreOutdatedAssembly\" option." x - | false -> File.ReadAllBytes(x) |> sha1CheckSum' - ) - |> Set.fold (fun a x -> a + x |> sha1CheckSum) String.Empty - + let hash = File.ReadAllBytes dllPath |> sha1CheckSum' let asm = Assembly.LoadFile(dllTempPath); let version = asm.GetName().Version |> fun y -> (y.Major, y.Minor, y.Build, y.Revision) - + { assembly = asm assemblyId = None dllName = dllName From c22804bf7e28b4f6b26c384ab07fb7397d2d7515 Mon Sep 17 00:00:00 2001 From: Morten Holt Date: Mon, 15 Apr 2024 14:27:32 +0200 Subject: [PATCH 4/7] Update release notes --- src/Delegate.Daxif/RELEASE_NOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Delegate.Daxif/RELEASE_NOTES.md b/src/Delegate.Daxif/RELEASE_NOTES.md index 96c417d..d4f23fa 100644 --- a/src/Delegate.Daxif/RELEASE_NOTES.md +++ b/src/Delegate.Daxif/RELEASE_NOTES.md @@ -1,6 +1,7 @@ # Release Notes ### 5.6.0 - April 04 2024 -* Update assembly comparison when determining if we want to update the assembly. Now also compares the version, and if the local version is larger we always sync the assembly file. +* Update assembly comparison when determining if we want to update the assembly. Compare the version of the local assembly to the version currently registered, if the local version is higher (by semver rules) than the registered version, update the assembly even if the hash matches. (@mkholt) +* Changed the hashing functionality to no longer load in the project files and dependencies, but instead taking a SHA1 sum of the assembly file. This removes the dependency on the project files, and makes the hashing more reliable. (@mkholt) ### 5.5.1 - May 23 2023 * Fixed 'useUniqueInstance' parameter to GetCrmServiceClient() with default value: 'false'. - Developer now has to actively enable multiple instances of service client. This is due to potential authentication issues if too many simultaneous tasks are spawning connections (such as WebResourceSync functionality) (@bo-stig-christensen) From b46dca6207b297482798da0ddba04e37d4fc5793 Mon Sep 17 00:00:00 2001 From: Morten Holt Date: Mon, 15 Apr 2024 14:27:52 +0200 Subject: [PATCH 5/7] Update date in release notes --- src/Delegate.Daxif/RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Delegate.Daxif/RELEASE_NOTES.md b/src/Delegate.Daxif/RELEASE_NOTES.md index d4f23fa..619fa18 100644 --- a/src/Delegate.Daxif/RELEASE_NOTES.md +++ b/src/Delegate.Daxif/RELEASE_NOTES.md @@ -1,5 +1,5 @@ # Release Notes -### 5.6.0 - April 04 2024 +### 5.6.0 - April 15 2024 * Update assembly comparison when determining if we want to update the assembly. Compare the version of the local assembly to the version currently registered, if the local version is higher (by semver rules) than the registered version, update the assembly even if the hash matches. (@mkholt) * Changed the hashing functionality to no longer load in the project files and dependencies, but instead taking a SHA1 sum of the assembly file. This removes the dependency on the project files, and makes the hashing more reliable. (@mkholt) From 0cbf84e2c3dab3f50308d72963fa655324f5d086 Mon Sep 17 00:00:00 2001 From: Morten Holt Date: Wed, 24 Apr 2024 15:00:51 +0200 Subject: [PATCH 6/7] Prettify the log message and change function name to actually make sense --- src/Delegate.Daxif/Modules/Plugins/Compare.fs | 9 +++++---- src/Delegate.Daxif/Modules/Plugins/MainHelper.fs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Delegate.Daxif/Modules/Plugins/Compare.fs b/src/Delegate.Daxif/Modules/Plugins/Compare.fs index f5fd3b5..34b9c04 100644 --- a/src/Delegate.Daxif/Modules/Plugins/Compare.fs +++ b/src/Delegate.Daxif/Modules/Plugins/Compare.fs @@ -138,19 +138,20 @@ let image (img: Image) (x: Entity) = /// Compares an assembly from CRM with the one containing the source code /// Returns true if the assembly in CRM is newer and the hash matches the one in the source code -let assembly (local: AssemlyLocal) (registered: AssemblyRegistration option) = +let registeredIsSameAsLocal (local: AssemlyLocal) (registered: AssemblyRegistration option) = registered ?|> fun y -> let log = ConsoleLogger.Global let environmentIsNewer = y.version .>= local.version - log.Verbose "Comparing assembly version. Registered >= local? %b" environmentIsNewer + log.Verbose "Registered version %s is %s than local version %s" + (y.version |> versionToString) (if environmentIsNewer then "newer" else "older") (local.version |> versionToString) let hashMatch = y.hash = local.hash - log.Verbose "Comparing assembly hash. Hash matches? %b" hashMatch + log.Verbose "Registered assembly hash %s local assembly hash" (if hashMatch then "matches" else "does not match") let isSameAssembly = environmentIsNewer && hashMatch - log.Verbose "Assembly is unchanged? %b" isSameAssembly + log.Verbose "Assembly will%s be updated" (if isSameAssembly then " not" else "") isSameAssembly ?| false \ No newline at end of file diff --git a/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs b/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs index d70f93e..151c256 100644 --- a/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs +++ b/src/Delegate.Daxif/Modules/Plugins/MainHelper.fs @@ -65,7 +65,7 @@ let localToMaps (plugins: Plugin seq) (customAPIs: CustomAPI seq) = /// Determine which operation we want to perform on the assembly let determineOperation (asmReg: AssemblyRegistration option) (asmLocal) : AssemblyOperation * Guid = match asmReg with - | Some asm when Compare.assembly asmLocal (Some asm) -> Unchanged, asm.id + | Some asm when Compare.registeredIsSameAsLocal asmLocal (Some asm) -> Unchanged, asm.id | Some asm -> Update, asm.id | None -> Create, Guid.Empty From b7eb9e2397c58ad158db2d43dfd04d75f80403fa Mon Sep 17 00:00:00 2001 From: Morten Holt Date: Thu, 25 Apr 2024 08:26:59 +0200 Subject: [PATCH 7/7] Update date in release note --- src/Delegate.Daxif/RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Delegate.Daxif/RELEASE_NOTES.md b/src/Delegate.Daxif/RELEASE_NOTES.md index 619fa18..af0c51a 100644 --- a/src/Delegate.Daxif/RELEASE_NOTES.md +++ b/src/Delegate.Daxif/RELEASE_NOTES.md @@ -1,5 +1,5 @@ # Release Notes -### 5.6.0 - April 15 2024 +### 5.6.0 - April 25 2024 * Update assembly comparison when determining if we want to update the assembly. Compare the version of the local assembly to the version currently registered, if the local version is higher (by semver rules) than the registered version, update the assembly even if the hash matches. (@mkholt) * Changed the hashing functionality to no longer load in the project files and dependencies, but instead taking a SHA1 sum of the assembly file. This removes the dependency on the project files, and makes the hashing more reliable. (@mkholt)