commit 83d8edd1334feca1e99678426473a542187960c4 Author: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Fri Jun 7 20:21:38 2024 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a66c69d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +~$* + +src/logs/* \ No newline at end of file diff --git a/distr/!README.docx b/distr/!README.docx new file mode 100644 index 0000000..d78dbd7 Binary files /dev/null and b/distr/!README.docx differ diff --git a/src/CD_GenerateManifest.ps1 b/src/CD_GenerateManifest.ps1 new file mode 100644 index 0000000..da1bd07 --- /dev/null +++ b/src/CD_GenerateManifest.ps1 @@ -0,0 +1,18 @@ +$version = Get-Content VERSION + +$config = @{ + 'Path' = '.\ConceptDeploy\ConceptDeploy.psd1' + 'Author' = 'IRBorisov' + 'CompanyName' = 'CIHT Concept' + 'ModuleVersion' = "$version" + 'RootModule' = 'ConceptDeploy.psm1' + 'FunctionsToExport' = @('Initialize-Python', 'Install-PythonLibs', 'Install-PSLatest', 'Push-PythonPackage', 'Push-DLL', 'Expand-TarGZ' + 'Get-ExchangePath', 'Get-ProjectsPath', 'Get-ServerDistr', 'Get-ConceptLocal', + 'Save-DistrManifest', 'Open-DistrManifest', 'Install-Product', 'Enable-Product', 'Disable-Product') + 'CmdletsToExport' = @() + 'VariablesToExport' = '' + 'AliasesToExport' = @() + 'Description' = 'CIHT Concept tools deployment module' +} + +New-ModuleManifest @config \ No newline at end of file diff --git a/src/CD_Install.ps1 b/src/CD_Install.ps1 new file mode 100644 index 0000000..62b5674 --- /dev/null +++ b/src/CD_Install.ps1 @@ -0,0 +1,10 @@ +# Install ConceptDeploy module for current user +Set-Variable -Name MODULE_NAME -Value 'ConceptDeploy' -Option Constant + +$modulePath = "$env:USERPROFILE\Documents\WindowsPowerShell\modules\$MODULE_NAME" +if (Test-Path -Path $modulePath) { + Remove-Item $modulePath -Recurse -Force +} +New-Item -Path $modulePath -ItemType Directory -Force | Out-Null + +Copy-Item -Path "$PSScriptRoot\$MODULE_NAME\*" -Destination $modulePath -Recurse \ No newline at end of file diff --git a/src/CD_Tests.ps1 b/src/CD_Tests.ps1 new file mode 100644 index 0000000..9293dc2 --- /dev/null +++ b/src/CD_Tests.ps1 @@ -0,0 +1,9 @@ +$VerbosePreference="Continue" + +Import-Module -Name .\ConceptDeploy -Force +# $python = Initialize-Python +# $python = 'python' +# Install-PythonLibs -Python $python +# & $python -m pip list + +# Push-DLL \ No newline at end of file diff --git a/src/ConceptDeploy/ConceptDeploy.psd1 b/src/ConceptDeploy/ConceptDeploy.psd1 new file mode 100644 index 0000000..25ea79d Binary files /dev/null and b/src/ConceptDeploy/ConceptDeploy.psd1 differ diff --git a/src/ConceptDeploy/ConceptDeploy.psm1 b/src/ConceptDeploy/ConceptDeploy.psm1 new file mode 100644 index 0000000..72924ed --- /dev/null +++ b/src/ConceptDeploy/ConceptDeploy.psm1 @@ -0,0 +1,9 @@ +$localPath = $script:MyInvocation.MyCommand.Path + +Get-ChildItem (Split-Path $localPath) -Filter '*.ps1' -Recurse | ForEach-Object { + . $_.FullName +} + +Get-ChildItem "$(Split-Path $localPath)" -Filter '*.ps1' -Recurse | ForEach-Object { + Export-ModuleMember -Function $_.BaseName +} \ No newline at end of file diff --git a/src/ConceptDeploy/Disable-Product.ps1 b/src/ConceptDeploy/Disable-Product.ps1 new file mode 100644 index 0000000..a3eba4d --- /dev/null +++ b/src/ConceptDeploy/Disable-Product.ps1 @@ -0,0 +1,44 @@ +function Disable-Product { + [CmdletBinding()] + Param( + [string] $product + ) + + if ($product -eq 'ExcelHelper') { + return RemoveExcelAddin 'CONCEPT.xlam' + } elseif ($product -eq 'WordHelper') { + return RemoveWordAddin 'CONCEPT.dotm' + } elseif ($product -eq 'Concept-Reports') { + return RemoveExcelAddin 'ConceptReport.xlam' + } elseif ($product -eq 'Concept-Maket') { + return RemoveWordAddin '_Maket.dotm' + } elseif ($product -eq 'Concept-NPA') { + return RemoveWordAddin '_Concept-NPA.dotm' + } elseif ($product -eq 'Concept-Mining') { + return RemoveWordAddin 'Parsers.dotm' + } elseif ($product -eq 'Concept-Markup') { + return RemoveWordAddin 'MARKUP.dotm' + } + return $false +} + +function TryDelete($fullName) { + try { + Remove-Item -Path $fullName + return $true + } catch { + return $false + } +} + +function RemoveExcelAddin($fileName) { + $fullName = "$($Env:APPDATA)\Microsoft\Excel\XLSTART\$fileName" + Write-Host "Removing addin $fullName" + return TryDelete $fullName +} + +function RemoveWordAddin($fileName) { + $fullName = "$($Env:APPDATA)\Microsoft\Word\STARTUP\$fileName" + Write-Host "Removing addin $fullName" + return TryDelete $fullName +} \ No newline at end of file diff --git a/src/ConceptDeploy/Enable-Product.ps1 b/src/ConceptDeploy/Enable-Product.ps1 new file mode 100644 index 0000000..2032632 --- /dev/null +++ b/src/ConceptDeploy/Enable-Product.ps1 @@ -0,0 +1,47 @@ +function Enable-Product { + [CmdletBinding()] + Param( + [string] $product, + [string] $localTools + ) + + if ($product -eq 'ExcelHelper') { + return CopyExcelAddin "$localTools\optional" 'CONCEPT.xlam' + } elseif ($product -eq 'WordHelper') { + return CopyWordAddin "$localTools\optional" 'CONCEPT.dotm' + } elseif ($product -eq 'Concept-Reports') { + return CopyExcelAddin "$localTools\optional" 'ConceptReport.xlam' + } elseif ($product -eq 'Concept-Maket') { + return CopyWordAddin "$localTools\optional" '_Maket.dotm' + } elseif ($product -eq 'Concept-NPA') { + return CopyWordAddin "$localTools\optional" '_Concept-NPA.dotm' + } elseif ($product -eq 'Concept-Mining') { + return CopyWordAddin "$localTools\optional" 'Parsers.dotm' + } elseif ($product -eq 'Concept-Markup') { + return CopyWordAddin "$localTools\optional" 'MARKUP.dotm' + } + return $false +} + +function TryCopy($source, $destination) { + try { + Copy-Item -Path $source -Destination $destination -Force -Recurse -ErrorAction Stop + return $true + } catch { + return $false + } +} + +function CopyExcelAddin($location, $fileName) { + $source = "$location\$fileName" + $destination = "$($Env:APPDATA)\Microsoft\Excel\XLSTART\" + Write-Host "Copying addin $fileName to $destination" + return TryCopy $source $destination +} + +function CopyWordAddin($location, $fileName) { + $source = "$location\$fileName" + $destination = "$($Env:APPDATA)\Microsoft\Word\STARTUP\" + Write-Host "Copying addin $fileName to $destination" + return TryCopy $source $destination +} \ No newline at end of file diff --git a/src/ConceptDeploy/Expand-TarGZ.ps1 b/src/ConceptDeploy/Expand-TarGZ.ps1 new file mode 100644 index 0000000..955b216 --- /dev/null +++ b/src/ConceptDeploy/Expand-TarGZ.ps1 @@ -0,0 +1,44 @@ +Function Expand-TarGZ { + [CmdletBinding()] + Param( + [string] $inFile, + [string] $outFolder = ($inFile -replace '\.tar.gz$','') + ) + + $tarFile = Expand-GZ -InFile $inFile + if (-not $tarFile) { + Write-Error "Cannot unpack gzip $inFile into $tarFile" + Exit + } + + if (-not (Get-Command Expand-7Zip -ErrorAction Ignore)) { + Install-Package -Scope CurrentUser -Force 7Zip4PowerShell > $null + } + Expand-7Zip $tarFile $outFolder + + Remove-Item $tarFile -Recurse -Force + return $outFolder +} + +Function Expand-GZ([string] $inFile) { + $outFile = ($inFile -replace '\.gz$','') + if (Test-Path -Path $outFile -PathType leaf) { + Remove-Item -Path $outFile -Force + } + $BUFFER_SIZE = 1024 + $inputStream = New-Object System.IO.FileStream $inFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read) + $outputStream = New-Object System.IO.FileStream $outFile, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None) + $gzipStream = New-Object System.IO.Compression.GzipStream $inputStream, ([IO.Compression.CompressionMode]::Decompress) + + $buffer = New-Object byte[]($BUFFER_SIZE) + while($true) { + $read = $gzipstream.Read($buffer, 0, $BUFFER_SIZE) + if ($read -le 0){break} + $outputStream.Write($buffer, 0, $read) + } + + $gzipStream.Close() + $outputStream.Close() + $inputStream.Close() + return $outFile +} \ No newline at end of file diff --git a/src/ConceptDeploy/Get-ConceptLocal.ps1 b/src/ConceptDeploy/Get-ConceptLocal.ps1 new file mode 100644 index 0000000..9726e27 --- /dev/null +++ b/src/ConceptDeploy/Get-ConceptLocal.ps1 @@ -0,0 +1,4 @@ +# Return path to local Concept CIHT files +function Get-ConceptLocal { + return "$Env:USERPROFILE\.concept" +} \ No newline at end of file diff --git a/src/ConceptDeploy/Get-ExchangePath.ps1 b/src/ConceptDeploy/Get-ExchangePath.ps1 new file mode 100644 index 0000000..dd41aeb --- /dev/null +++ b/src/ConceptDeploy/Get-ExchangePath.ps1 @@ -0,0 +1,10 @@ +# Get exchange server folder for CIHT CONCEPT +function Get-ExchangePath { + $server = '\\fs1.concept.ru\Exchange' + if (-not (Test-Path -Path $server)) { + Write-Error "Failed to locate server $server. Ensure VPN is running" + Exit + } else { + return $server + } +} \ No newline at end of file diff --git a/src/ConceptDeploy/Get-ProjectsPath.ps1 b/src/ConceptDeploy/Get-ProjectsPath.ps1 new file mode 100644 index 0000000..4470c73 --- /dev/null +++ b/src/ConceptDeploy/Get-ProjectsPath.ps1 @@ -0,0 +1,10 @@ +# Get projects server folder for CIHT CONCEPT +function Get-ProjectsPath { + $server = '\\fs1.concept.ru\projects' + if (-not (Test-Path -Path $server)) { + Write-Error "Failed to locate server $server. Ensure VPN is running" + Exit + } else { + return $server + } +} \ No newline at end of file diff --git a/src/ConceptDeploy/Get-ServerDistr.ps1 b/src/ConceptDeploy/Get-ServerDistr.ps1 new file mode 100644 index 0000000..726841e --- /dev/null +++ b/src/ConceptDeploy/Get-ServerDistr.ps1 @@ -0,0 +1,10 @@ +# Get server code distribution folder for CIHT CONCEPT +function Get-ServerDistr { + $server = '\\fs1.concept.ru\projects\10 Автоматизация деятельности\!Concept' + if (-not (Test-Path -Path $server)) { + Write-Error "Failed to locate server $server. Ensure VPN is running" + Exit + } else { + return $server + } +} \ No newline at end of file diff --git a/src/ConceptDeploy/Initialize-Python.ps1 b/src/ConceptDeploy/Initialize-Python.ps1 new file mode 100644 index 0000000..16e4721 --- /dev/null +++ b/src/ConceptDeploy/Initialize-Python.ps1 @@ -0,0 +1,165 @@ +function Initialize-Python { + [CmdletBinding()] + Param( + [string] $minimalVersion = '3.12.2', + [string] $installPath = 'C:\Tools\Python312-venv', + [string] $pythonInstall = 'C:\Tools\Python312', + [string] $pythonDistr = '' + ) + $python = SearchInstalledEnvironment -TargetPath $installPath -MinimalVersion $minimalVersion + if ($python -ne '') { + return $python + } + $python = SearchLocalInstallation -TargetPath $pythonInstall -MinimalVersion $minimalVersion + if ($python -eq '') { + Write-Host "Failed to locate local installation of python" + $python = SearchUserPath -MinimalVersion $minimalVersion + } + if ($python -eq '') { + Write-Host "Failed to locate python in PATH variable" + $python = SearchUserRegistry -MinimalVersion $minimalVersion + } + if ($python -eq '') { + Write-Host "Failed to locate python in Windows registry" + $python = InstallLocalPython -Version $minimalVersion -TargetPath $pythonInstall -PythonDistr $pythonDistr + } + if ($python -eq '') { + return '' + } + CreateVirtualEnvironment -Python $python -TargetPath $installPath + return "$installPath\Scripts\python.exe" +} + +function SearchInstalledEnvironment($targetPath, $minimalVersion) { + $python = "$targetPath\Scripts\python.exe" + if (-not (Test-Path -Path $python -PathType Leaf)) { + Write-Host "Failed to locate Python venv at $targetPath" + return '' + } + $installedVersion = GetPythonVersion $python + if(!$installedVersion -or + $installedVersion -eq '' -or + [System.Version] $installedVersion -lt [System.Version] $minimalVersion) { + Write-Host "Removing invalid python env: $targetPath" -ForegroundColor DarkRed + Remove-Item $targetPath -Recurse + return '' + } else { + Write-Host "Found Python environment $installedVersion : $python" + return $python + } +} + +function SearchLocalInstallation($targetPath, $minimalVersion) { + $python = "$targetPath\python.exe" + if (-not (Test-Path -Path $python -PathType Leaf)) { + return '' + } + $installedVersion = GetPythonVersion $python + if(!$installedVersion -or + $installedVersion -eq '' -or + [System.Version] $installedVersion -ne [System.Version] $minimalVersion) { + return '' + } + Write-Host "Found installed python $installedVersion : $python" + return $python +} + +function SearchUserPath($minimalVersion) { + $globalVersion = GetPythonVersion 'python' + if(!$globalVersion -or + $globalVersion -eq '' -or + [System.Version] $globalVersion -ne [System.Version] $minimalVersion) { + return '' + } + Write-Host "Found globally installed python: $globalVersion" + return 'python' +} + +function SearchUserRegistry($minimalVersion) { + if ([Environment]::Is64BitProcess) { + $pythonRegistry = 'HKCU:\SOFTWARE\Python\PythonCore\3.12\InstallPath' + if(!$pythonRegistry) { + $pythonRegistry = 'HKLM:\SOFTWARE\Python\PythonCore\3.12\InstallPath' + } + } else { + $pythonRegistry = 'HKCU:\SOFTWARE\WOW6432Node\Python\PythonCore\3.12-32\InstallPath' + if(!$pythonRegistry) { + $pythonRegistry = 'HKLM:\SOFTWARE\WOW6432Node\Python\PythonCore\3.12-32\InstallPath' + } + } + $python = Get-ItemPropertyValue -Path $pythonRegistry -Name 'ExecutablePath' -ErrorAction 'SilentlyContinue' + if (-not $python) { + return '' + } + $version = GetPythonVersion $python + if(!$version -or + $version -eq '' -or + [System.Version] $version -lt [System.Version] $minimalVersion) { + return '' + } + Write-Host "Found installed local python: $python" -ForegroundColor DarkGreen + return $python +} + +function InstallLocalPython($version, $targetPath, $pythonDistr = '') { + if ($pythonDistr -eq '') { + $installer = DowloadPython $version + } else { + $installer = $pythonDistr + } + + $arguments = "/quiet InstallAllUsers=0 Include_test=0 Include_doc=0 Include_launcher=0 TargetDir=$targetPath" + $process = (Start-Process -FilePath $installer -ArgumentList $arguments -PassThru -NoNewWindow) + $process.WaitForExit() + + if ($pythonDistr -eq '') { + Remove-Item $installer -ErrorAction SilentlyContinue + } + + $python = "$targetPath\python.exe" + if (-not (Test-Path -Path $python -PathType Leaf)) { + Write-Error "Failed to install local python: $installer" + return '' + } else { + Write-Host "Installed local python: $targetPath" -ForegroundColor DarkGreen + return $python + } +} + +function GetPythonVersion($pythonExe) { + $eap = $ErrorActionPreference + $ErrorActionPreference = 'SilentlyContinue' + $version = & $pythonExe --version + $ErrorActionPreference = $eap + if (!$version -or $version -eq '' -or $LASTEXITCODE -ne 0) { + return '' + } else { + return $version.Split(' ')[1] + } +} + +function DowloadPython($version) { + $pythonUrl = GetPythonURL $version + $pythonTemp = "$Env:TEMP\installer-python-$version.exe" + Write-Host "Downloading python installer to $pythonTemp" + try { + (New-Object System.Net.WebClient).DownloadFile($pythonUrl, $pythonTemp) + } catch [System.Net.WebException] { + Write-Error "An exception was caught when trying to download Python: $($_.Exception.Message)" + Exit + } + return $pythonTemp +} + +function CreateVirtualEnvironment($python, $targetPath) { + & $python -m venv $targetPath + Write-Host "Created virtual environment $targetPath" -ForegroundColor DarkGreen +} + +function GetPythonURL($version) { + if ([Environment]::Is64BitProcess) { + return "https://www.python.org/ftp/python/$version/python-$version-amd64.exe" + } else { + return = "https://www.python.org/ftp/python/$version/python-$version.exe" + } +} \ No newline at end of file diff --git a/src/ConceptDeploy/Install-PSLatest.ps1 b/src/ConceptDeploy/Install-PSLatest.ps1 new file mode 100644 index 0000000..1393a42 --- /dev/null +++ b/src/ConceptDeploy/Install-PSLatest.ps1 @@ -0,0 +1,4 @@ +# Download and install latest version of powershell +function Install-PSLatest { + Invoke-Expression "& { $(Invoke-RestMethod https://aka.ms/install-powershell.ps1) } -UseMSI -Quiet" +} \ No newline at end of file diff --git a/src/ConceptDeploy/Install-Product.ps1 b/src/ConceptDeploy/Install-Product.ps1 new file mode 100644 index 0000000..9f1fe44 --- /dev/null +++ b/src/ConceptDeploy/Install-Product.ps1 @@ -0,0 +1,122 @@ +# Install concept product +function Install-Product { + [CmdletBinding()] + Param( + [string] $product, + [string] $distr, + [string] $localTemplates, + [string] $localTools + ) + + if ($product -eq 'ExcelHelper') { + return InstallExcelAddinOptional $distr 'CONCEPT.xlam' $localTools + } elseif ($product -eq 'WordHelper') { + return $(InstallWordAddinOptional $distr 'CONCEPT.dotm' $localTools -and + InstallUserModel $distr 'banned-words.txt') + } elseif ($product -eq 'Concept-Maket') { + return InstallWordAddinOptional $distr '_Maket.dotm' $localTools + } elseif ($product -eq 'Concept-Reports') { + return InstallExcelAddinOptional $distr "ConceptReport.xlam" $localTools + } elseif ($product -eq 'Concept-Hierarchy') { + return InstallTemplate $distr '30 Иерархизатор.vstm' $localTemplates + } elseif ($product -eq 'Concept-Blocks') { + return $(InstallTemplate $distr '20 Концепт-Блоки.vstm' $localTemplates -and + InstallTemplate $distr 'Технологии\Блоки-Excel.xltx' $localTemplates -and + InstallTemplate $distr 'Технологии\Блоки-Word.dotx' $localTemplates) + } elseif ($product -eq 'Concept-Defs') { + return $(InstallTemplate $distr '22 Карта понятий.vstm' $localTemplates -and + InstallTemplate $distr 'Технологии\Определения-Excel.xltx' $localTemplates) + } elseif ($product -eq 'Concept-NPA') { + Write-Host "Copying file !Реестр НПА.xlsm" + Copy-Item -Path "$distr\data\!Реестр НПА.xlsm" -Destination "$localTools\optional\" -Force + return $(InstallTemplate $distr '60 НПА UI.xltm' $localTemplates -and + InstallTemplate $distr '21 Схема Реестра НПА.vstm' $localTemplates -and + InstallWordAddinOptional $distr '_Concept-NPA.dotm' $localTools) + } elseif ($product -eq 'Concept-Mining') { + return $(InstallTemplate $distr '55 Майнинг.xltm' $localTemplates -and + InstallWordAddinOptional $distr 'Parsers.dotm' $localTools -and + InstallLocalModel $distr 'ActionVerbs.txt' $localTools) + } elseif ($product -eq 'Concept-Markup') { + return $(InstallTemplate $distr 'Разметка' $localTemplates -and + InstallWordAddinOptional $distr 'MARKUP.dotm' $localTools) + } + return $false +} + +function TryCopy($source, $destination) { + try { + Copy-Item -Path $source -Destination $destination -Force -Recurse -ErrorAction Stop + return $true + } catch { + return $false + } +} + +function InstallUserModel($distr, $fileName) { + $source = "$distr\models\$fileName" + $destination = "$(Get-ConceptLocal)\models\" + Write-Host "Update user model $fileName at $destination" + return TryCopy $source $destination +} + +function InstallLocalModel($distr, $fileName, $localTools) { + $source = "$distr\models\$fileName" + $destination = "$localTools\models\" + Write-Host "Copying model file $fileName to $destination" + return TryCopy $source $destination +} + +function InstallTemplate($distr, $fileName, $templates) { + $source = "$distr\data\Templates\$fileName" + $destination = "$templates\" + Write-Host "Copying template $fileName to $destination" + return TryCopy $source $destination +} + +function InstallExcelAddin($distr, $fileName) { + $source = "$distr\data\Add-ins\Excel\$fileName" + $destination = "$($Env:APPDATA)\Microsoft\Excel\XLSTART\" + Write-Host "Copying addin $fileName to $destination" + return TryCopy $source $destination +} + +function InstallExcelAddinOptional($distr, $fileName, $localTools) { + $source = "$distr\data\Add-ins\Excel\$fileName" + + $destination1 = "$localTools\optional\" + Write-Host "Copying optional addin $fileName to $destination1" + if (-not (TryCopy $source $destination1)) { + return $false + } + + $destination2 = "$($Env:APPDATA)\Microsoft\Excel\XLSTART\" + if (Test-Path -Path "$destination2\$fileName" -PathType Leaf) { + Write-Host "Copying optional addin $fileName to $destination2" + return TryCopy $source $destination2 + } + return $true +} + +function InstallWordAddin($distr, $fileName) { + $source = "$distr\data\Add-ins\Word\$fileName" + $destination = "$($Env:APPDATA)\Microsoft\Word\STARTUP\" + Write-Host "Copying addin $fileName to $destination" + return TryCopy $source $destination +} + +function InstallWordAddinOptional($distr, $fileName, $localTools) { + $source = "$distr\data\Add-ins\Word\$fileName" + + $destination1 = "$localTools\optional\" + Write-Host "Copying optional addin $fileName to $destination1" + if (-not (TryCopy $source $destination1)) { + return $false + } + + $destination2 = "$($Env:APPDATA)\Microsoft\Word\STARTUP\" + if (Test-Path -Path "$destination2\$fileName" -PathType Leaf) { + Write-Host "Copying optional addin $fileName to $destination2" + return TryCopy $source $destination2 + } + return $true +} \ No newline at end of file diff --git a/src/ConceptDeploy/Install-PythonLibs.ps1 b/src/ConceptDeploy/Install-PythonLibs.ps1 new file mode 100644 index 0000000..c4d33ef --- /dev/null +++ b/src/ConceptDeploy/Install-PythonLibs.ps1 @@ -0,0 +1,42 @@ +# Install Python libraries used by CIHT CONCEPT tools + +$global:globalLibs = @( + 'pywin32', + 'pandas' +) + +$global:localLibs = @( + 'cctext', + 'vbatopy' +) + +function Install-PythonLibs { + [CmdletBinding()] + Param( + [Parameter(Mandatory=$true)] + [string]$python, + [string]$localDistr = '' + ) + Write-Host "`nUpgrading pip tools..." -ForegroundColor DarkGreen + & $python -m pip install --upgrade pip + InstallGlobalLibs + InstallLocalLibs -libsPath $localDistr +} + +function InstallGlobalLibs { + foreach ($lib in $globalLibs) { + Write-Host "`nInstalling from global repo: $lib" -ForegroundColor DarkGreen + & $python -m pip install -U $lib + } +} + +function InstallLocalLibs([string]$libsPath) { + if ($libsPath -eq '') { + . "$PSScriptRoot\Get-ServerDistr.ps1" + $libsPath = "$(Get-ServerDistr)\src" + } + foreach ($lib in $localLibs) { + Write-Host "`nInstalling from local folder: $libsPath\$lib" -ForegroundColor DarkGreen + & $python -m pip install -U "$libsPath\$lib" + } +} \ No newline at end of file diff --git a/src/ConceptDeploy/Open-DistrManifest.ps1 b/src/ConceptDeploy/Open-DistrManifest.ps1 new file mode 100644 index 0000000..23dcc67 --- /dev/null +++ b/src/ConceptDeploy/Open-DistrManifest.ps1 @@ -0,0 +1,16 @@ +function Open-DistrManifest() { + [CmdletBinding()] + Param( + [Parameter(Mandatory=$true)] + [string] $fileName + ) + + Write-Host "Open distribution manifest ... $fileName" + $result = [ordered] @{} + if (-not (Test-Path -Path $fileName -PathType Leaf)) { + return $result + } + $jsonObj = Get-Content -Path $fileName -Raw | ConvertFrom-Json + $jsonObj.PSObject.Properties | ForEach-Object { $result[$_.Name] = $_.Value } + return $result +} \ No newline at end of file diff --git a/src/ConceptDeploy/Push-DLL.ps1 b/src/ConceptDeploy/Push-DLL.ps1 new file mode 100644 index 0000000..3f5fb13 --- /dev/null +++ b/src/ConceptDeploy/Push-DLL.ps1 @@ -0,0 +1,22 @@ +# Push Dynamic library to server and distr +function Push-DLL { + [CmdletBinding()] + Param( + [Parameter(Mandatory=$true)] + [string] $dllName, + [Parameter(Mandatory=$true)] + [string] $dllPath, + [bool] $pushServer = $true, + [bool] $pushDistr = $true + ) + + . "$PSScriptRoot\Get-ServerDistr.ps1" + . "$PSScriptRoot\Get-ExchangePath.ps1" + + if ($pushServer) { + Copy-Item -Path "$dllPath\$dllName.dll" -Destination "$(Get-ServerDistr)\dll\" + } + if ($pushDistr) { + Copy-Item -Path "$dllPath\$dllName.dll" -Destination "$(Get-ExchangePath)\ConceptDistr\dll\" + } +} \ No newline at end of file diff --git a/src/ConceptDeploy/Push-PythonPackage.ps1 b/src/ConceptDeploy/Push-PythonPackage.ps1 new file mode 100644 index 0000000..e07007c --- /dev/null +++ b/src/ConceptDeploy/Push-PythonPackage.ps1 @@ -0,0 +1,49 @@ +# Push python module to Concept server and distr +function Push-PythonPackage { + [CmdletBinding()] + Param( + [Parameter(Mandatory=$true)] + [string] $packageName, + [Parameter(Mandatory=$true)] + [string] $packageRoot, + [bool] $pushServer = $true, + [bool] $pushDistr = $true + ) + + . "$PSScriptRoot\Get-ServerDistr.ps1" + . "$PSScriptRoot\Get-ExchangePath.ps1" + + if ($pushServer) { + UpdatePythonIn -PackageName $packageName -PackageRoot $packageRoot -ServerRoot "$(Get-ServerDistr)\src" + } + if ($pushDistr) { + UpdatePythonIn -PackageName $packageName -PackageRoot $packageRoot -ServerRoot "$(Get-ExchangePath)\ConceptDistr\src" + } +} + +function UpdatePythonIn($packageName, $packageRoot, $serverRoot) { + . "$PSScriptRoot\Expand-TarGZ.ps1" + + $targz = Get-Childitem -Path "$packageRoot\output\$packageName\*.tar.gz" -Name + if (-not $targz) { + Write-Error "Cannot find unique source distribution in $packageRoot\output\$packageName\" + Exit + } + $targz = "$packageRoot\output\$packageName\$targz" + + $destination = "$serverRoot\$packageName" + if (Test-Path -Path $destination) { + Remove-Item $destination -Recurse -Force + } + + $unpacked = Expand-TarGZ -InFile $targz + if (-not $unpacked) { + Write-Error "Cannot unpack $targz" + Exit + } + + Move-Item -Path "$unpacked\$packageName-*\*" -Destination "$unpacked" + Remove-Item -Path "$unpacked\$packageName-*" + + Move-Item -Path "$unpacked" -Destination "$destination" +} \ No newline at end of file diff --git a/src/ConceptDeploy/Save-DistrManifest.ps1 b/src/ConceptDeploy/Save-DistrManifest.ps1 new file mode 100644 index 0000000..ae522e2 --- /dev/null +++ b/src/ConceptDeploy/Save-DistrManifest.ps1 @@ -0,0 +1,12 @@ +function Save-DistrManifest() { + [CmdletBinding()] + Param( + [Parameter(Mandatory=$true)] + $manifestData, + [Parameter(Mandatory=$true)] + [string] $fileName + ) + + Write-Host "Save distribution manifest ... $fileName" + $manifestData | ConvertTo-Json | Out-File -Encoding "UTF8" $fileName +} \ No newline at end of file diff --git a/src/DeployDistributives.ps1 b/src/DeployDistributives.ps1 new file mode 100644 index 0000000..442dea9 --- /dev/null +++ b/src/DeployDistributives.ps1 @@ -0,0 +1,68 @@ +# Prepare and deploy distribution files for standalone distributive +Set-StrictMode -Version 3.0 +Set-Variable -Name PYTHON_DOWNLOAD_VERSION -Value '3.12.2' +Set-Variable -Name MODULE_NAME -Value 'ConceptDeploy' + +function DeployDistributives { + & .\CD_GenerateManifest.ps1 + Import-Module -Name ".\$MODULE_NAME" -Force + + Write-Host "Preparing python environment clone..." + $preparedEnvironment = PrepareEnvironemnt + + Write-Host "Creating python packages archive from local environment..." + $envZip = 'C:\Tools\Python312-venv.zip' + Compress-Archive -Path "$preparedEnvironment\*" -DestinationPath $envZip -Force + Remove-Item $preparedEnvironment -Recurse -Force + + Write-Host "Downloading python $PYTHON_DOWNLOAD_VERSION installer..." + $pythonDistr = DowloadPython $PYTHON_DOWNLOAD_VERSION + + $destination = "$(Get-ExchangePath)\ConceptDistr\distr" + if (-not (Test-Path -Path $destination)) { + New-Item -Path $destination -ItemType Directory -Force | Out-Null + } + + Write-Host "Moving files to destination... $destination" + Copy-Item -Path $pythonDistr -Destination "$destination\" -Force + Copy-Item -Path $envZip -Destination "$destination\" -Force + + Remove-Item $pythonDistr -Force + Remove-Item $envZip -Force +} + +function PrepareEnvironemnt { + $coreFiles = 'activate', 'activate.bat', 'Activate.ps1', 'deactivate.bat', 'python.exe', 'pythonw.exe', 'pythonw_d.exe', 'python_d.exe' + $tools = 'C:\Tools' + $original = "$tools\Python312-venv" + $envClone = "$tools\Python312-cln" + if (-not (Test-Path -Path $original)) { + Exit 1 + } + if (Test-Path -Path $envClone) { + Remove-Item $envClone -Recurse -Force + } + New-Item -Path $envClone -ItemType Directory -Force | Out-Null + Copy-Item -Path "$original\Include" -Destination "$envClone\Include" -Recurse + Copy-Item -Path "$original\Lib" -Destination "$envClone\Lib" -Recurse + Copy-Item -Path "$original\Scripts" -Destination "$envClone\Scripts" -Recurse + + Get-ChildItem $envClone -Recurse | Where-Object{$_.Name.EndsWith('.pyc')} | Remove-Item + Get-ChildItem -Path "$envClone\Scripts\*" -Recurse -Include $coreFiles | Remove-Item + + return $envClone +} + +function DowloadPython($version) { + $pythonUrl = "https://www.python.org/ftp/python/$version/python-$version-amd64.exe" + $pythonTemp = "$Env:TEMP\python-$version-amd64.exe" + try { + (New-Object System.Net.WebClient).DownloadFile($pythonUrl, $pythonTemp) + } catch [System.Net.WebException] { + Write-Error "An exception was caught when trying to download Python: $($_.Exception.Message)" + Exit + } + return $pythonTemp +} + +DeployDistributives \ No newline at end of file diff --git a/src/DeployServer.ps1 b/src/DeployServer.ps1 new file mode 100644 index 0000000..fdb587a --- /dev/null +++ b/src/DeployServer.ps1 @@ -0,0 +1,34 @@ +# Deploy ConceptDeploy module files to server storage +Set-StrictMode -Version 3.0 +Set-Variable -Name MODULE_NAME -Value 'ConceptDeploy' + +& .\CD_GenerateManifest.ps1 +Import-Module -Name ".\$MODULE_NAME" -Force + +$conceptServer = "$(Get-ServerDistr)" +$serverSrc = "$conceptServer\src\$MODULE_NAME" +if (Test-Path -Path $serverSrc) { + Remove-Item $serverSrc -Recurse -Force +} +New-Item -Path "$serverSrc\$MODULE_NAME" -ItemType Directory -Force | Out-Null + +Copy-Item -Path "$PSScriptRoot\CD_Install.ps1" -Destination "$serverSrc\" +Copy-Item -Path "$PSScriptRoot\VERSION" -Destination "$serverSrc\" +Copy-Item -Path "$PSScriptRoot\$MODULE_NAME\*" -Destination "$serverSrc\$MODULE_NAME" -Recurse + +$distr = "$(Get-ExchangePath)\ConceptDistr" +$exchangeSrc = "$distr\src\$MODULE_NAME" +if (Test-Path -Path $exchangeSrc) { + Remove-Item $exchangeSrc -Recurse -Force +} +New-Item -Path $exchangeSrc -ItemType Directory -Force | Out-Null + +Copy-Item -Path "$PSScriptRoot\InstallAllProducts.ps1" -Destination "$distr\" +Copy-Item -Path "$PSScriptRoot\UpdateConceptProducts.ps1" -Destination "$distr\" +Copy-Item -Path "$PSScriptRoot\UninstallConceptProducts.ps1" -Destination "$distr\" +Copy-Item -Path "$PSScriptRoot\install_launcher.bat" -Destination "$distr\install.bat" +Copy-Item -Path "$PSScriptRoot\uninstall.bat" -Destination "$distr\uninstall.bat" +Copy-Item -Path "$PSScriptRoot\install_from_server.bat" -Destination "$distr\install_from_server.bat" +Copy-Item -Path "$PSScriptRoot\install_standalone.bat" -Destination "$distr\install_standalone.bat" +Copy-Item -Path "$PSScriptRoot\..\distr\!README.docx" -Destination "$distr\!README.docx" +Copy-Item -Path "$serverSrc\*" -Destination $exchangeSrc -Recurse \ No newline at end of file diff --git a/src/InstallAllProducts.ps1 b/src/InstallAllProducts.ps1 new file mode 100644 index 0000000..8e9dbab --- /dev/null +++ b/src/InstallAllProducts.ps1 @@ -0,0 +1,344 @@ +# Install Concept products from local distr + +# Setup constants +Set-Variable -Name OFFICE_REGISTRY -Value 'HKCU:\SOFTWARE\Microsoft\Office\16.0' -Option Constant +Set-Variable -Name DOMAINS_REGISTRY -Value 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains' -Option Constant +Set-Variable -Name SERVER_PATH -Value '\\fs1.concept.ru\' -Option Constant +Set-Variable -Name TEMPLATES_PATH -Value "$Env:APPDATA\Microsoft\Шаблоны" -Option Constant +Set-Variable -Name TEMPLATES_NORMAL_PATH -Value "$Env:APPDATA\Microsoft\Templates" -Option Constant + +$word = If (Test-Path "${OFFICE_REGISTRY}\Word") {"${OFFICE_REGISTRY}\Word"} Else {''} +$excel = If (Test-Path "${OFFICE_REGISTRY}\Excel") {"${OFFICE_REGISTRY}\Excel"} Else {''} +$visio = If (Test-Path "${OFFICE_REGISTRY}\Visio") {"${OFFICE_REGISTRY}\Visio"} Else {''} + +function InstallAllProducts { + [CmdletBinding()] + Param( + [Parameter(Mandatory=$true)] + [string] $distrPath, + [switch] $skipShortcuts = $false, + [switch] $standalone = $false + ) + + Set-StrictMode -Version 3.0 + + if (-not (Test-Path -Path $distrPath)) { + Write-Error "Distribution path does not exist: $distrPath" + Exit 1 + } + + $InformationPreference="Continue" + + StartLog + + Write-Host "Starting installation procedure for Concept technologies" -ForegroundColor DarkGreen + Write-Host "Sources: $distrPath" + Write-Host "Skip shortcuts: $skipShortcuts`n" + + Write-Host "Dependencies status:" -ForegroundColor DarkGreen + StateSystemStatus + + $driveLetter = MountPathDrive $distrPath + $distr = "${driveLetter}`:" + $localHome = "C:\Tools" + + Write-Host "`nEnsure core is updated..." -ForegroundColor DarkGreen + PrepareCore $distr $distrPath $standalone $localHome + + Write-Host "`nSetup SecureLocation options for $SERVER_PATH" -ForegroundColor DarkGreen + SetupTrustedLocations $SERVER_PATH + + Write-Host "`nInitializing template path for Office applications: $TEMPLATES_PATH" -ForegroundColor DarkGreen + InitializeTemplates $TEMPLATES_PATH + + Write-Host "Update templates at $TEMPLATES_PATH" -ForegroundColor DarkGreen + UpdateTemplates "${distr}\data\Templates" $TEMPLATES_PATH $localHome + Copy-Item -Path "$TEMPLATES_PATH\Normal.dotm" -Destination "${TEMPLATES_NORMAL_PATH}\Normal.dotm" + Copy-Item -Path "$TEMPLATES_PATH\NormalEmail.dotm" -Destination "${TEMPLATES_NORMAL_PATH}\NormalEmail.dotm" + + Write-Host "`nUpdating products..." -ForegroundColor DarkGreen + UpdateProducts $distr $TEMPLATES_PATH $localHome + + if (-not ($skipShortcuts)) { + Write-Host "`nInstalling desktop shortcuts" -ForegroundColor DarkGreen + Copy-Item -Path "${distr}\data\Shortcuts\*" ` + -Destination "$($Env:USERPROFILE)\Desktop\" ` + -Exclude (Get-ChildItem "$($Env:USERPROFILE)\Desktop") -Force -Recurse + } + + Write-Host "`nClean up procedure ..." -ForegroundColor Blue + Remove-PSDrive -Name $driveLetter + + Write-Host "`nInstallation complete" -ForegroundColor Green + + StopLog +} + +function StartLog { + $ErrorActionPreference='SilentlyContinue' + Stop-Transcript | Out-Null + $ErrorActionPreference = "Continue" + + $dateSuffix = (Get-Date -UFormat "%Y%m%d_%I-%M-%S").ToString() + $logFile = "${PSScriptRoot}\logs\${dateSuffix}_$($Env:USERNAME).txt" + Start-Transcript -Path $logFile -IncludeInvocationHeader +} + +function StopLog { + Stop-Transcript +} + +function CheckProcess($processName) { + $app = Get-Process $processName -ErrorAction SilentlyContinue + return -not ($null -eq $app) +} + +function StateSystemStatus() { + if ($word) { + Write-Host "Word running: $(CheckProcess 'WINWORD')" + } else { + Write-Host 'Word not found' -ForegroundColor DarkRed + } + if ($excel) { + Write-Host "Excel running: $(CheckProcess 'EXCEL')" + } else { + Write-Host 'Excel not found' -ForegroundColor DarkRed + } + if ($visio) { + Write-Host "Visio running: $(CheckProcess 'VISIO')" + } else { + Write-Host 'Visio not found' -ForegroundColor DarkRed + } +} + +function MountPathDrive($targetPath) { + Write-Host "`nCreating temporary drive for ${targetPath}" -ForegroundColor DarkGreen + [char[]] $takenDrive = (Get-PSDrive -Name [E-Z]).Name + $driveLetter = ([char[]] (69..90)).Where({ $_ -notin $takenDrive }, 'First')[0] + if (!$driveLetter) { + Write-Error "No available drive letter" + Exit 1 + } + New-PSDrive -Name $driveLetter -PSProvider FileSystem -Root $targetPath -Scope Global | Out-Null + Write-Host "Drive ${driveLetter} mounted" + return $driveLetter +} + +function UpdateTemplates($source, $destination, $localHome) { + Copy-Item -Path "${source}\*" -Destination "$destination\" -Force -Recurse + + $templatesFile = "${localHome}\script\templates_list.txt" + $sourcePrefix = $(Resolve-Path $source).ProviderPath + Write-Host "Save temapltes list to $templatesFile" + Get-ChildItem -Recurse $source | Where-Object { ! $_.PSIsContainer } | Select-Object FullName ` + | ForEach-Object { $_.FullName.Replace("$sourcePrefix\",'') } ` + | Out-File -Encoding "UTF8" $templatesFile +} + +function UpdateProducts($distr, $templates, $localHome) { + $serverManifest = "${distr}\distribution_manifest.json" + $serverJSON = Open-DistrManifest $serverManifest + + $localManifest = "C:\Tools\distribution_manifest.json" + $localJSON = Open-DistrManifest $localManifest + $firstRun = $localJSON.Count -eq 0 + + Write-Host "`n" + foreach ($product in $serverJSON.Keys) { + $serverVersion = [System.Version] $serverJSON.$product + if ($localJSON.Contains($product)) { + $localVersion = [System.Version] $localJSON.$product + } else { + $localVersion = [System.Version] "0.0.0" + } + if ($serverVersion -eq $localVersion) { + Write-Host "Product ${product}: ${serverVersion} ... up to date`n" -ForegroundColor Gray + } elseif (Install-Product $product $distr $templates $localHome) { + Write-Host "Product ${product}: ${serverVersion} ... updated from ${localVersion}`n" -ForegroundColor DarkGreen + $localJSON[$product] = $serverJSON.$product + } else { + Write-Host "Product ${product}: ${serverVersion} ... failed to update from ${localVersion}`n" -ForegroundColor DarkRed + } + } + if ($firstRun) { + Write-Host "`nFirst time run. Enabling default addins" -ForegroundColor DarkGreen + Enable-Product 'WordHelper' $localHome | Out-Null + Enable-Product 'ExcelHelper' $localHome | Out-Null + Enable-Product 'Concept-Reports' $localHome | Out-Null + } + + Save-DistrManifest $localJSON $localManifest +} + +function PrepareCore($distrMount, $distrPath, $standalone, $localHome) { + if (CheckConceptDeploy $distrMount) { + Import-Module -Name ConceptDeploy + Write-Host "ConceptDeploy already installed: $((Get-Module -Name 'ConceptDeploy').Version)" -ForegroundColor Gray + } else { + Write-Host "Installing Powershell library: ConceptDeploy" -ForegroundColor DarkGreen + . "$distrMount\src\ConceptDeploy\CD_Install.ps1" + Import-Module -Name ConceptDeploy + Write-Host "Installed version: ConceptDeploy $((Get-Module -Name 'ConceptDeploy').Version)" + } + + if (-not (Test-Path -Path "${localHome}\script\")) { + New-Item -Path "${localHome}\script\" -ItemType Directory -Force | Out-Null + } + Copy-Item -Path "${distrMount}\UpdateConceptProducts.ps1" -Destination "${localHome}\script\" + Copy-Item -Path "${distrMount}\UninstallConceptProducts.ps1" -Destination "${localHome}\script\" + Copy-Item -Path "${distrMount}\uninstall.bat" -Destination "${localHome}\script\uninstall-concept.bat" + Write-Host "Installed scripts to $localHome\script\" -ForegroundColor Gray + + Write-Host "`nEnsure Python environement is updated..." -ForegroundColor DarkGreen + + $python = '' + if ($standalone) { + if ([Environment]::Is64BitProcess) { + $python = Initialize-Python -PythonDistr "${distrPath}\distr\python-3.12.2-amd64.exe" + } else { + Write-Host "32-bit system do not support Standalone installation mode" -ForegroundColor Red + } + } else { + $python = Initialize-Python + } + + if ($python -eq '') { + Write-Host "Skip python libraries because could not initialize python" -ForegroundColor DarkRed + } elseif ($standalone) { + $pythonFolder = (Get-item $python).Directory.Parent.FullName + Expand-Archive -Path "${distrMount}\distr\Python312-venv.zip" -DestinationPath $pythonFolder -Force + } else { + Install-PythonLibs -python $python -localDistr "${distrPath}\src" + } + + Write-Host "`nUpdate dynamic libraries at ${localHome}" -ForegroundColor DarkGreen + if (-not (Test-Path -Path "${localHome}\dll")) { + New-Item -Path "${localHome}\dll" -ItemType Directory -Force | Out-Null + } + Copy-Item -Path "${distrMount}\dll" -Destination "${localHome}\" -Force -Recurse + + $conceptLocal = Get-ConceptLocal + if (-not (Test-Path -Path "${conceptLocal}\models")) { + New-Item -Path "${conceptLocal}\models" -ItemType Directory -Force | Out-Null + } + if (-not (Test-Path -Path "${localHome}\optional")) { + New-Item -Path "${localHome}\optional" -ItemType Directory -Force | Out-Null + } + if (-not (Test-Path -Path "${localHome}\models")) { + New-Item -Path "${localHome}\models" -ItemType Directory -Force | Out-Null + } +} + +function CheckConceptDeploy($distr) { + $currentModule = Get-Module -ListAvailable ConceptDeploy + if (!$currentModule) { + return $false + } + $newVersion = [System.Version] (Get-Content -Path "${distr}\src\ConceptDeploy\VERSION") + $oldVersion = $currentModule.Version + return $oldVersion -ge $newVersion +} + +function InitializeTemplates($templates) { + $eap = $ErrorActionPreference + $ErrorActionPreference = 'SilentlyContinue' + + if (-not (Test-Path -Path $templates)) { + New-Item -ItemType Directory -Path $templates -Force | Out-Null + Write-Host "Created office templates directory: ${templates}" + } + + if ($word) { + if(-not ( + Get-ItemProperty "${word}\Options" | + ForEach-Object { if ($_ -like '*PersonalTemplates*') { return $true } } + )) { + New-ItemProperty -Path "${word}\Options" -Name 'PersonalTemplates' -Value $templates -PropertyType ExpandString -Force + } else { + Set-ItemProperty -Path "${word}\Options" -Name 'PersonalTemplates' -Value $templates + } + } + if ($excel) { + if(-not ( + Get-ItemProperty "${excel}\Options" | + ForEach-Object { if ($_ -like '*PersonalTemplates*') { return $true } } + )) { + New-ItemProperty -Path "${excel}\Options" -Name 'PersonalTemplates' -Value $templates -PropertyType ExpandString -Force + } else { + Set-ItemProperty -Path "${excel}\Options" -Name 'PersonalTemplates' -Value $templates + } + } + if ($visio) { + if(-not ( + Get-ItemProperty "${visio}\Application" | + ForEach-Object { if ($_ -like '*PersonalTemplates*') { return $true } } + )) { + New-ItemProperty -Path "${visio}\Options" -Name 'PersonalTemplates' -Value $templates -PropertyType ExpandString -Force + } else { + Set-ItemProperty -Path "${visio}\Application" -Name 'PersonalTemplates' -Value $templates + } + } + $ErrorActionPreference = $eap +} + +function SetupTrustedLocations($serverLocation) { + if ($word) { + if (AddTrustedLocation $word $serverLocation) { + Write-Host 'Word ... DONE' + } else { + Write-Host 'Word ... EXISTS' + } + } + if ($excel) { + if (AddTrustedLocation $excel $serverLocation) { + Write-Host 'Excel ... DONE' + } else { + Write-Host 'Excel ... EXISTS' + } + } + if ($visio) { + if (AddTrustedLocation $visio $serverLocation) { + Write-Host 'Visio ... DONE' + } else { + Write-Host 'Visio ... EXISTS' + } + } + $conceptDomain = "${DOMAINS_REGISTRY}\concept.ru\fs1" + if (Test-Path -Path $conceptDomain) { + Write-Host 'Intranet settings ... EXISTS' + } else { + New-Item -Path "${DOMAINS_REGISTRY}\concept.ru" -ItemType Directory | Out-Null + New-Item -Path $conceptDomain -ItemType Directory | Out-Null + New-ItemProperty -Path $conceptDomain -Name 'file' -Value 1 | Out-Null + Write-Host 'Intranet settings ... DONE' + } +} + +function AddTrustedLocation($target, $location) { + $registryPath = "${target}\Security\Trusted Locations" + if (-not(Test-Path -Path $registryPath)) { + if (-not(Test-Path -Path "${target}\Security")){ + New-Item -Path "${target}\Security" -ItemType Directory | Out-Null + } + New-Item -Path $registryPath -ItemType Directory | Out-Null + } + Set-ItemProperty -Path $registryPath -Name 'AllowNetworkLocations' -Value 1 + if ( + Get-ChildItem -Path $registryPath -Recurse | + ForEach-Object { if ($_.GetValue('Path') -eq $location) { return $true } } + ) { + return $false + } else { + $index = 3 + while (Test-Path -Path "${registryPath}\Location${index}") { $index++ } + $folder = "${registryPath}\Location${index}" + New-Item -Path $folder -ItemType Directory | Out-Null + New-ItemProperty -Path $folder -Name 'Path' -Value $location | Out-Null + New-ItemProperty -Path $folder -Name 'Description' -Value 'Сетевой путь КОНЦЕПТ' | Out-Null + New-ItemProperty -Path $folder -Name 'AllowSubfolders' -Value 1 | Out-Null + return $true + } +} + +# InstallAllProducts "D:\test\ConceptDistr" +# SetupTrustedLocations \\fs1.concept.ru\ \ No newline at end of file diff --git a/src/UninstallConceptProducts.ps1 b/src/UninstallConceptProducts.ps1 new file mode 100644 index 0000000..b699844 --- /dev/null +++ b/src/UninstallConceptProducts.ps1 @@ -0,0 +1,127 @@ +# Uninstall Concept products + +function UninstallConceptProducts { + [CmdletBinding()] + Param( + ) + Set-StrictMode -Version 3.0 + $InformationPreference="Continue" + + StartLog + + Write-Host "Starting uninstallation procedure for Concept technologies`n" -ForegroundColor DarkGreen + + PrepareExternalProcesses + + if (-not (Get-Module -ListAvailable ConceptDeploy)) { + Write-Host "Missing ConceptDeploy library" + Exit 1 + } + Import-Module -Name ConceptDeploy + + $localHome = "C:\Tools" + + Write-Host "`nRemoving Concept Products" -ForegroundColor DarkGreen + $localJSON = Open-DistrManifest "$localHome\distribution_manifest.json" + foreach ($product in $localJSON.Keys) { + Disable-Product $product | Out-Null + Write-Host "Product $product has been removed`n" -ForegroundColor Gray + } + TryRemove "$localHome\optional\" + TryRemove "$localHome\models\" + TryRemove "$localHome\distribution_manifest.json" + + Write-Host "`nRemoving user specific files" -ForegroundColor DarkGreen + TryRemove "$(Get-ConceptLocal)\" + + $templates = GetTemplatesFolder + $fileList = Get-Content "$localHome\script\templates_list.txt" + Write-Host "Remove templates at $templates" -ForegroundColor DarkGreen + foreach ($file in $fileList) { + if ($file -ne 'Normal.dotm' -and $file -ne 'NormalEmail.dotm') { + TryRemove "$templates\$file" + } + } + TryRemove "$localHome\script\templates_list.txt" + + Write-Host "`nRemoving core files" + TryRemove "$localHome\script\UpdateConceptProducts.ps1" + TryRemove "$localHome\script\UninstallConceptProducts.ps1" + TryRemove "$localHome\script\uninstall-concept.bat" + TryRemove "$localHome\Python312-venv\" + TryRemove "$localHome\dll\" + + Write-Host "`nRemoving ConceptDeploy library" -ForegroundColor DarkGreen + Remove-Module -Name ConceptDeploy + TryRemove "$env:USERPROFILE\Documents\WindowsPowerShell\modules\ConceptDeploy" + + Write-Host "`nRemoving desktop shortcuts" -ForegroundColor DarkGreen + TryRemove "$($Env:USERPROFILE)\Desktop\Everything - поиск по имени.lnk" + TryRemove "$($Env:USERPROFILE)\Desktop\DocFetcher - поиск по содержимому.lnk" + TryRemove "$($Env:USERPROFILE)\Desktop\Double Commander.lnk" + + Write-Host "`nUninstallation complete" -ForegroundColor Green + + StopLog +} + +function StartLog { + $ErrorActionPreference="SilentlyContinue" + Stop-Transcript | Out-Null + $ErrorActionPreference = "Continue" + + $dateSuffix = (Get-Date -UFormat "%Y%m%d_%I-%M-%S").ToString() + $logFile = "$PSScriptRoot\logs\$($dateSuffix)_CP-uninstall_$($Env:USERNAME).txt" + Start-Transcript -Path $logFile -IncludeInvocationHeader +} + +function StopLog { + Stop-Transcript +} + +function CheckProcess($processName) { + $app = Get-Process $processName -ErrorAction SilentlyContinue + return -not ($null -eq $app) +} + +function TryRemove($path) { + if (Test-Path -Path $path) { + Write-Host "Removing $path..." -ForegroundColor Gray + Remove-Item -Path $path -Recurse -Force + } else { + Write-Host "Failed to locate $path..." -ForegroundColor Red + } +} + +function PrepareExternalProcesses { + Write-Host "Close blocking applications..." -ForegroundColor DarkGreen + if ($(CheckProcess WINWORD)) { + Stop-Process -Name WINWORD -Force + } + if ($(CheckProcess EXCEL)) { + Stop-Process -Name EXCEL -Force + } + if ($(CheckProcess VISIO)) { + Stop-Process -Name VISIO -Force + } + if ($(CheckProcess Exteor)) { + Stop-Process -Name Exteor -Force + } + Write-Host "Word running: $(CheckProcess WINWORD)" + Write-Host "Excel running: $(CheckProcess EXCEL)" + Write-Host "Visio running: $(CheckProcess VISIO)" + Write-Host "Exteor running: $(CheckProcess Exteor)" +} + +function GetTemplatesFolder { + $officeRK = 'HKCU:\SOFTWARE\Microsoft\Office\16.0' + $defaultTemplates = "$($Env:APPDATA)\Microsoft\Шаблоны" + + $templates = Get-ItemPropertyValue -Path "$officeRK\Word\Options" -Name 'PersonalTemplates' -ErrorAction 'SilentlyContinue' + if (!$templates -or [string]::IsNullOrWhiteSpace($templates)) { + $templates = $defaultTemplates + } + return $templates +} + +# UninstallConceptProducts \ No newline at end of file diff --git a/src/UpdateConceptProducts.ps1 b/src/UpdateConceptProducts.ps1 new file mode 100644 index 0000000..2fd3772 --- /dev/null +++ b/src/UpdateConceptProducts.ps1 @@ -0,0 +1,162 @@ +# Install Concept products from local distr + +function UpdateConceptProducts { + [CmdletBinding()] + Param( + [Parameter(Mandatory=$true)] + [string] $distrPath, + [string[]] $products = $(, 'all') + ) + + Set-StrictMode -Version 3.0 + + if ($products.Count -lt 1) { + Write-Host 'Missing products list' + Exit 1 + } + + StartLog + + Write-Host "Starting update procedure for Concept technologies" -ForegroundColor DarkGreen + Write-Host "Sources: $distrPath" + Write-Host "Requested products: $products" + + $updates = New-Object System.Collections.Generic.List[System.Object] + $enable_products = New-Object System.Collections.Generic.List[System.Object] + $disable_products = New-Object System.Collections.Generic.List[System.Object] + if ($products[0] -eq 'all') { + $updates.Add('all') + } else { + foreach ($p in $products) { + if ($p.EndsWith('-') -or $p.EndsWith('+')) { + $product_id = $p.Substring(0, $p.Length - 1) + } else { + $product_id = $p + } + if ($p.EndsWith('-')) { + $disable_products.Add($product_id) + } else { + $updates.Add($product_id) + $enable_products.Add($product_id) + } + } + } + Write-Host "Check updates: $updates" + Write-Host "Enable products: $enable_products" + Write-Host "Disable products: $disable_products" + + if (Test-Path -Path "filesystem::$distrPath\distribution_manifest.json") { + Write-Host "Check path $distrPath\distribution_manifest.json ... ACCESS GRANTED" -ForegroundColor DarkGreen + UpdateProducts $distrPath $updates + + } else { + Write-Host "Check path $distrPath\distribution_manifest.json ... NO ACCESS" -ForegroundColor DarkRed + Write-Host "Skipping product updates..." -ForegroundColor DarkRed + } + + $tools = 'C:\Tools' + foreach ($product in $enable_products) { + if (Enable-Product $product $tools) { + Write-Host "Product $($product): enabled`n" -ForegroundColor DarkGreen + } else { + Write-Host "Product $($product): failed to enable`n" -ForegroundColor DarkRed + } + } + + $tools = 'C:\Tools' + foreach ($product in $disable_products) { + if (Disable-Product $product) { + Write-Host "Product $($product): disabled`n" -ForegroundColor DarkGreen + } else { + Write-Host "Product $($product): failed to disable`n" -ForegroundColor DarkRed + } + } + + Write-Host "`nUpdate complete" -ForegroundColor Green + + StopLog +} + +function StartLog { + $ErrorActionPreference="SilentlyContinue" + Stop-Transcript | Out-Null + $ErrorActionPreference = "Continue" + + $dateSuffix = (Get-Date -UFormat "%Y%m%d_%I-%M-%S").ToString() + $logFile = "$PSScriptRoot\logs\$($dateSuffix)_CP-update_$($Env:USERNAME).txt" + Start-Transcript -Path $logFile -IncludeInvocationHeader +} + +function StopLog { + Stop-Transcript +} + +function UpdateProducts($distrPath, $products) { + $driveLetter = MountPathDrive $distrPath + $distr = "$driveLetter`:" + + if (-not(CheckConceptDeploy $distr)) { + Write-Host "Installing Powershell library: ConceptDeploy" -ForegroundColor DarkGreen + . "$driveLetter\src\ConceptDeploy\CD_Install.ps1" + } + Import-Module -Name ConceptDeploy + Write-Host "Using ConceptDeploy: $((Get-Module -Name 'ConceptDeploy').Version)" -ForegroundColor Gray + + if ($products.Count -ne 0) { + $serverManifest = "$distr\distribution_manifest.json" + $serverJSON = Open-DistrManifest $serverManifest + + $localManifest = "C:\Tools\distribution_manifest.json" + $localJSON = Open-DistrManifest $localManifest + + if ($products[0] -eq 'all') { + $products = $serverJSON.Keys + } + foreach ($product in $products) { + $serverVersion = [System.Version] $serverJSON.$product + if ($localJSON.Contains($product)) { + $localVersion = [System.Version] $localJSON.$product + } else { + $localVersion = [System.Version] "0.0.0" + } + if ($serverVersion -eq $localVersion) { + Write-Host "Product $($product): $serverVersion ... up to date`n" -ForegroundColor Gray + } elseif (Install-Product $product $distr $templates $localTools) { + Write-Host "Product $($product): $serverVersion ... updated from $localVersion`n" -ForegroundColor DarkGreen + $localJSON[$product] = $serverJSON.$product + } else { + Write-Host "Product $($product): $serverVersion ... failed to update from $localVersion`n" -ForegroundColor DarkRed + } + } + Save-DistrManifest $localJSON $localManifest + } + + Write-Host "`nUnmount $driveLetter" -ForegroundColor DarkGreen + Remove-PSDrive -Name $driveLetter +} + +function MountPathDrive($targetPath) { + Write-Host "`nCreating temporary drive for $targetPath" -ForegroundColor DarkGreen + [char[]] $takenDrive = (Get-PSDrive -Name [E-Z]).Name + $driveLetter = ([char[]] (69..90)).Where({ $_ -notin $takenDrive }, 'First')[0] + if (!$driveLetter) { + Write-Error "No available drive letter" + Exit 1 + } + New-PSDrive -Name $driveLetter -PSProvider FileSystem -Root $targetPath -Scope Global | Out-Null + Write-Host "Drive $driveLetter mounted" + return $driveLetter +} + +function CheckConceptDeploy($distr) { + $currentModule = Get-Module -ListAvailable ConceptDeploy + if (!$currentModule) { + return $false + } + $newVersion = [System.Version] (Get-Content -Path "$distr\src\ConceptDeploy\VERSION") + $oldVersion = $currentModule.Version + return $oldVersion -ge $newVersion +} + + +# UpdateConceptProducts -DistrPath '\\fs1.concept.ru\Exchange\ConceptDistr' 'Concept-Maket-', 'Concept-Mining+' \ No newline at end of file diff --git a/src/VERSION b/src/VERSION new file mode 100644 index 0000000..13d683c --- /dev/null +++ b/src/VERSION @@ -0,0 +1 @@ +3.0.1 \ No newline at end of file diff --git a/src/install_from_server.bat b/src/install_from_server.bat new file mode 100644 index 0000000..f4e25bd --- /dev/null +++ b/src/install_from_server.bat @@ -0,0 +1,7 @@ +@echo off + +@pushd %~dp0 +PowerShell -NoProfile -ExecutionPolicy Bypass -Command ". "%cd%\InstallAllProducts.ps1"; InstallAllProducts \\fs1.concept.ru\Exchange\ConceptDistr;" +@popd + +pause \ No newline at end of file diff --git a/src/install_launcher.bat b/src/install_launcher.bat new file mode 100644 index 0000000..f1d9c3e --- /dev/null +++ b/src/install_launcher.bat @@ -0,0 +1,7 @@ +@echo off + +@pushd %~dp0 +PowerShell -NoProfile -ExecutionPolicy Bypass -Command ". "%cd%\InstallAllProducts.ps1"; InstallAllProducts %cd%;" +@popd + +pause \ No newline at end of file diff --git a/src/install_standalone.bat b/src/install_standalone.bat new file mode 100644 index 0000000..30a4a55 --- /dev/null +++ b/src/install_standalone.bat @@ -0,0 +1,7 @@ +@echo off + +@pushd %~dp0 +PowerShell -NoProfile -ExecutionPolicy Bypass -Command ". "%cd%\InstallAllProducts.ps1"; InstallAllProducts %cd% -Standalone -SkipShortcuts;" +@popd + +pause \ No newline at end of file diff --git a/src/uninstall.bat b/src/uninstall.bat new file mode 100644 index 0000000..d5c6fd8 --- /dev/null +++ b/src/uninstall.bat @@ -0,0 +1,7 @@ +@echo off + +@pushd %~dp0 +PowerShell -NoProfile -ExecutionPolicy Bypass -Command ". "%cd%\UninstallConceptProducts.ps1"; UninstallConceptProducts;" +@popd + +pause \ No newline at end of file