We want to update our installations of SourceTree Enterprise (on Windows 10 and 11; ST version 3.4.18) to version 3.4.20.
When rolling out the new version we observed that the user.config found under
%userprofile%\AppData\Local\Atlassian\SourceTree.exe_Url_someHashLikeString\3.4.18.0 is not transferred over to \3.4.20.0 which results in all settings reverting back to default.
We know that it is possible to ship a pre-configured SourceTree.exe.config with modified settings, but this is only useful for some general settings, not for all of them.
So the question is: How can we make sure our users don't lose their configurations every time we update SourceTree?
Back then I had spent way too much time on a solution that is acceptable, sort of.
It is a PowerShell script that will be executed whenever a user logs on. It checks for a registry key and compares it to the currently installed version of SourceTree and will continue if the current version is newer. It then performs a few checks and copies over the config from the last version, if applicable.
This way of doing it has a few shortcomings, though:
Overall, we're contemplating reverting to individual user-scoped installations and just have the users be responsible for their own housekeeping.
For reference, the script I referred to:
# Copy SourceTree Config on logon, only requires user-level privileges
#
# By Jonas Denter 11.11.2024
# Lauer & Weiss GmbH
#
# add a module to facilitate searching the registry
Import-Module "$PSScriptRoot\search-registry.ps1" -Force
# where the this script will write its log to:
$Logfile = "$env:USERPROFILE\sourceTreeConfig.log"
# where in the registry this script will persist its status for subsequent calls
# results in: HKCU:\SOFTWARE\LuW\SourceTreeUpdateScript.VersionLastExecuted
$RegBase = "HKCU:\SOFTWARE"
$RegLuW = "LuW"
$RegSubContainer = "SourceTreeUpdateScript"
$KeyName = "VersionLastExecuted"
# where the SourceTree user config is expected to be found (LOCAL):
$ConfigBaseFolderLocal = "$env:LOCALAPPDATA\Atlassian\SourceTree.exe_Url_yjhzxxhcu2ommegrdorjf3q1oqllqxiq"
# where the SourceTree user config is expected to be found (Roaming):
$ConfigBaseFolderRoaming = "$env:APPDATA\Atlassian\SourceTree.exe_Url_yjhzxxhcu2ommegrdorjf3q1oqllqxiq"
Function LogWrite
{
Param ([string]$Message)
Write-Output $Message
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
$logstring = "$Stamp $Message"
Add-content $Logfile -value $logstring
}
# try to find the "Installed Apps" entry for SourceTree
$regSearchResult = Search-Registry -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\*\Products\" -Recurse -ValueDataRegex "Sourcetree"
# bail if search returns empty -> SourceTree not installed?
if (-Not ($regSearchResult)) {
LogWrite "Registry entry not found. Is SourceTree not installed?"
Exit 0
}
$stInstallationContainer = $regSearchResult.Key
# if the entry exists, it will contain the key "DisplayVersion", which has the version information for the latest SourceTree installation - thus exactly what is needed here
$versionKeyName = "DisplayVersion"
$targetVersion = [System.Version]$stInstallationContainer.GetValue($versionKeyName, $null)
# bail if DisplayVersion does not exist - script likely needs to be adapted then
if (-Not ($targetVersion)) {
LogWrite ("Registry key '" + $versionKeyName + "' not found. Please investigate.")
Exit 0
}
LogWrite ("Latest installed version of SourceTree: " + $targetVersion.ToString())
# test for existence of registry containers and create them if needed
$LuWContainer = ("$RegBase\$RegLuW")
if (-not (Test-Path $LuWContainer)) {
New-Item -Path $RegBase -Name $RegLuW
}
$SubCont = ("$LuWContainer\$RegSubContainer")
if (Test-Path $SubCont) {
$contPath = Get-Item -Path $SubCont
} else {
$contPath = New-Item -Path $LuWContainer -Name $RegSubContainer
}
# test for existence of registry key and create it if needed
if ($null -eq $contPath.GetValue($KeyName, $null)) {
LogWrite ("Key '" + $KeyName + "' does not exist in container " + $SubCont + ". Creating it now.")
$newItemPropertySplat = @{
Path = $contPath.PsPath
Name = $KeyName
PropertyType = 'String'
Value = "3.4.18.0"
}
New-ItemProperty @newItemPropertySplat
}
# retrieve the version this script last executed for
$lastRunVersion = [System.Version]$contPath.GetValue($KeyName, "0.0.0")
LogWrite ("The SourceTree config has last been copied for version: " + $lastRunVersion.ToString())
if ($lastRunVersion -ge $targetVersion) {
LogWrite ("VersionLastExecuted exceeds VersionCurrentlyInstalled. Exiting now.")
Exit 0
}
# find available config versions
$configFolders = Get-ChildItem -Directory -Path $ConfigBaseFolderLocal
$configVersions = $configFolders.Name | ForEach-Object {[System.Version]$_}
# filter out all versions greater than or equal to the current version, and sort the remaining entries
$configVersionsBelowTarget = $configVersions | Where-Object { $_ -lt $targetVersion }
$configVersionsBelowTarget = $configVersionsBelowTarget | Sort-Object -Descending
# if the target (=latest installed) version already has a config folder, we need to ask
# as this means either the script has already run or the user created the folder
$targetConfigExists = $configVersions -contains $targetVersion
# if the target version already exists, ask what to do,
# otherwise just take the largest version below targetVersion as source
if ($targetConfigExists) {
LogWrite "The current version of SourceTree already has a config folder. What now?"
# User selection:
# - Via Listbox: Nicer, can be customized to make clear what is asked
# - Out-GridView: No work needed, but not as clear
$configVersions = $configVersions | Sort-Object -Descending
$sourceVersion = $configVersions | Out-GridView -OutputMode Single -Title "Select version for SourceTree's old config:"
if ($null -ne $sourceVersion) {
LogWrite ("User selection: '" + $sourceVersion.ToString() + "'" )
}
} else {
$sourceVersion = $configVersionsBelowTarget | Select-Object -First 1
}
if ($null -eq $sourceVersion) {
LogWrite "No source folder selected or no source folders available. Exiting now."
} elseif ($sourceVersion -eq $targetVersion) {
LogWrite "Source folder equals target folder. Nothing to do."
} else {
# copy source folder and rename it to target version
# AppData\Local:
$sourceFolder = $ConfigBaseFolderLocal + "\" + $sourceVersion.ToString()
$targetFolder = $ConfigBaseFolderLocal + "\" + $targetVersion.ToString()
# Workaround for an inconsistency in Copy-Item:
# New-Item will create the folder or just return it if it already exists
# see https://github.com/PowerShell/PowerShell/issues/2934
Copy-Item -Path "$sourceFolder\*" -Destination (New-Item -Force -ItemType Directory $targetFolder) -Recurse -Force
# AppData\Roaming:
$sourceFolder = $ConfigBaseFolderRoaming + "\" + $sourceVersion.ToString()
$targetFolder = $ConfigBaseFolderRoaming + "\" + $targetVersion.ToString()
Copy-Item -Path "$sourceFolder\*" -Destination (New-Item -Force -ItemType Directory $targetFolder) -Recurse -Force
LogWrite ("Copied folder " + $sourceVersion.ToString() + " to " + $targetVersion.ToString() )
}
# Update registry entry with current version
Set-ItemProperty -Path $contPath.PSPath -Name $KeyName -Value $targetVersion.ToString()
LogWrite ("Updated " + $contPath + "\" + $KeyName + " from " + $lastRunVersion.ToString() + " to " + $targetVersion.ToString() )
It needs an additional cmdlet search-registry.ps1, which I sourced from: StackOverflow
Hope this will be helpful to anyone.
Welcome to the Atlassian Community @Jonas Denter !
I know it has been some time since you posted your question.
Please let us know if you were able to fix the issue.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I've written up an answer that describes how we "solved" the problem. But as it can be considered a workaround at best, I'll leave the question unanswered.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.