Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(persist): Using [Array] to persist file #3209

Closed
wants to merge 12 commits into from
33 changes: 33 additions & 0 deletions bucket/nvm-array.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"homepage": "https://github.com/coreybutler/nvm-windows",
"version": "1.1.7",
"url": "https://github.com/coreybutler/nvm-windows/releases/download/1.1.7/nvm-noinstall.zip",
"bin": "nvm.exe",
"persist": [
"nodejs",
[
"elevate.cmd",
"nodejs\\elevate.cmd"
],
[
"elevate.vbs",
"nodejs\\elevate.vbs"
],
["settings.txt", "", "root: $persist_dir\\nodejs`r`narch: $architecture`r`nproxy: none"]
],
"env_add_path": "nodejs\\nodejs",
"env_set": {
"NVM_HOME": "$dir",
"NVM_SYMLINK": "$persist_dir\\nodejs\\nodejs"
},
"hash": "md5:00acf86e40c5f038cca8c383b9d2c207",
"notes": "You'll need to restart powershell/cmd to have it reload Environment Variables so nvm will work correctly",
"checkver": "github",
"autoupdate": {
"url": "https://github.com/coreybutler/nvm-windows/releases/download/$version/nvm-noinstall.zip",
"hash": {
"url": "$url.checksum.txt",
"find": "([a-fA-F0-9]{32})"
}
}
}
43 changes: 43 additions & 0 deletions bucket/nvm-object.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"homepage": "https://github.com/coreybutler/nvm-windows",
"version": "1.1.7",
"url": "https://github.com/coreybutler/nvm-windows/releases/download/1.1.7/nvm-noinstall.zip",
"bin": "nvm.exe",
"persist": [
{
"name": "nodejs",
"type": "directory",
"method": "link"
},
{
"name": "elevate.cmd",
"type": "file",
"target": "nodejs\\elevate.cmd"
},
{
"name": "elevate.vbs",
"type": "file",
"target": "nodejs\\elevate.vbs"
},
{
"name": "settings.txt",
"type": "file",
"contents": "root: $persist_dir\\nodejs`r`narch: $architecture`r`nproxy: none"
}
],
"env_add_path": "nodejs\\nodejs",
"env_set": {
"NVM_HOME": "$dir",
"NVM_SYMLINK": "$persist_dir\\nodejs\\nodejs"
},
"hash": "md5:00acf86e40c5f038cca8c383b9d2c207",
"notes": "You'll need to restart powershell/cmd to have it reload Environment Variables so nvm will work correctly",
"checkver": "github",
"autoupdate": {
"url": "https://github.com/coreybutler/nvm-windows/releases/download/$version/nvm-noinstall.zip",
"hash": {
"url": "$url.checksum.txt",
"find": "([a-fA-F0-9]{32})"
}
}
}
27 changes: 23 additions & 4 deletions lib/core.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ function url_remote_filename($url) {
return $basename
}

function ensure($dir) { if(!(test-path $dir)) { mkdir $dir > $null }; resolve-path $dir }
function ensure($dir) { if(!(test-path $dir)) { New-Item $dir -ItemType Directory -Force | Out-Null }; resolve-path $dir }
function fullpath($path) { # should be ~ rooted
$executionContext.sessionState.path.getUnresolvedProviderPathFromPSPath($path)
}
Expand Down Expand Up @@ -285,16 +285,34 @@ function isFileLocked([string]$path) {
}

function is_directory([String] $path) {
return (Test-Path $path) -and (Get-Item $path) -is [System.IO.DirectoryInfo]
return (Test-Path $path) -and (Get-Item -Path $path) -is [System.IO.DirectoryInfo]
}

function movedir($from, $to) {
function create_junction([String] $link, [String] $target) {
if (!(Test-Path $link) -and (is_directory $target)) {
New-Item -ItemType Junction -Path $link -Target $target | Out-Null
$dirInfo = New-Object System.IO.DirectoryInfo($link)
$dirInfo.Attributes = $dirInfo.Attributes -bor [System.IO.FileAttributes]::ReadOnly
return $true
}
return $false
}

function create_hardlink([String] $link, [String] $target) {
if (!(Test-Path $link) -and (Test-Path $target) -and !(is_directory $target)) {
New-Item -ItemType HardLink -Path $link -Target $target | Out-Null
return $true
}
return $false
}

function movedir($from, $to, $par = "") {
$from = $from.trimend('\')
$to = $to.trimend('\')

$proc = New-Object System.Diagnostics.Process
$proc.StartInfo.FileName = 'robocopy.exe'
$proc.StartInfo.Arguments = "`"$from`" `"$to`" /e /move"
$proc.StartInfo.Arguments = "`"$from`" `"$to`" /e /move " + $par
$proc.StartInfo.RedirectStandardOutput = $true
$proc.StartInfo.RedirectStandardError = $true
$proc.StartInfo.UseShellExecute = $false
Expand All @@ -311,6 +329,7 @@ function movedir($from, $to) {
# wait for robocopy to terminate its threads
1..10 | ForEach-Object {
if (Test-Path $from) {
Remove-Item -Path $from.FullName -Recurse -Force
Start-Sleep -Milliseconds 100
}
}
Expand Down
213 changes: 142 additions & 71 deletions lib/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -927,14 +927,11 @@ function link_current($versiondir) {
abort "Error: Version 'current' is not allowed!"
}

if(test-path $currentdir) {
# remove the junction
attrib -R /L $currentdir
& "$env:COMSPEC" /c rmdir $currentdir
# recreate new junction
if (Test-Path $currentdir) {
Remove-Item $currentdir -Recurse -Force
}

& "$env:COMSPEC" /c mklink /j $currentdir $versiondir | out-null
attrib $currentdir +R /L
create_junction $currentdir $versiondir | Out-Null
return $currentdir
}

Expand All @@ -950,11 +947,7 @@ function unlink_current($versiondir) {
if(test-path $currentdir) {
write-host "Unlinking $(friendly_path $currentdir)"

# remove read-only attribute on link
attrib $currentdir -R /L

# remove the junction
& "$env:COMSPEC" /c "rmdir $currentdir"
Remove-Item $currentdir -Recurse -Force
return $currentdir
}
return $versiondir
Expand Down Expand Up @@ -1126,90 +1119,168 @@ function show_suggestions($suggested) {
}

# Persistent data
function persist_def($persist) {
if ($persist -is [Array]) {
$source = $persist[0]
$target = $persist[1]
} else {
$source = $persist
$target = $null
}

if (!$target) {
$target = $source
}

return $source, $target
}

function persist_data($manifest, $original_dir, $persist_dir) {
$persist = $manifest.persist
if($persist) {
$persist_dir = ensure $persist_dir

if ($persist -is [String]) {
if ($persist -isnot [Array]) {
$persist = @($persist);
}

$persist | ForEach-Object {
$source, $target = persist_def $_
if ($_ -is [String] -or $_ -is [Array]) {
$persist_def = persist_def_arr $_
} else {
$persist_def = persist_def_obj $_
}
debug $persist_def
persist_helper @persist_def
}
}
}

write-host "Persisting $source"
function persist_def_obj($persist) {
$persist_def = @{}
$persist_def.source = $persist.name.TrimEnd('/').TrimEnd('\')
if ($persist.target) {
$persist_def.target = $persist.target
} else {
$persist_def.target = $persist_def.source
}

$source = $source.TrimEnd("/").TrimEnd("\\")
if ($persist.type) {
$type = $persist.type
} elseif ($persist.name -match "[/\\]$") {
$type = "directory"
} else {
$type = "file"
}
if ($null -eq $persist.glue) {
$glue = "`r`n"
} else {
$glue = $persist.glue
}
if ($type -eq "file") {
$persist_def.contents = $persist.contents -join $glue
} else {
$persist_def.contents = $null
}

$source = fullpath "$dir\$source"
$target = fullpath "$persist_dir\$target"
if ($persist.method) {
$persist_def.method = $persist.method
}

# if we have had persist data in the store, just create link and go
if (Test-Path $target) {
# if there is also a source data, rename it (to keep a original backup)
if (Test-Path $source) {
Move-Item -Force $source "$source.original"
}
# we don't have persist data in the store, move the source to target, then create link
} elseif (Test-Path $source) {
# ensure target parent folder exist
$null = ensure (Split-Path -Path $target)
Move-Item $source $target
# we don't have neither source nor target data! we need to crate an empty target,
# but we can't make a judgement that the data should be a file or directory...
# so we create a directory by default. to avoid this, use pre_install
# to create the source file before persisting (DON'T use post_install)
} else {
$target = New-Object System.IO.DirectoryInfo($target)
ensure $target | Out-Null
}
if ($persist.encoding -and ($type -eq "file")) {
$persist_def.encoding = $persist.encoding
}

# create link
if (is_directory $target) {
# target is a directory, create junction
& "$env:COMSPEC" /c "mklink /j `"$source`" `"$target`"" | out-null
attrib $source +R /L
return $persist_def
}
function persist_def_arr($persist) {
if ($persist -is [Array]) {
# if $persist is Array, use its length to determine its type
switch ($persist.Count) {
# lenght = 1, type is file
1 { $source = $persist[0]; $target = $null; $contents = "" }
# length = 2, type is directory and persist to different name
2 { $source = $persist[0]; $target = $persist[1]; $contents = $null }
# length > 2, type is file and the remaining rows are file contents
Default { $source = $persist[0]; $target = $persist[1]; $contents = $persist[2..($persist.Count-1)] -join "`r`n" }
}
} else {
# $persist is directory
$source = $persist
$target = $null
$contents = $null
}

if (!$target) {
$target = $source
}

$persist_def = @{
source = $source.TrimEnd('/').TrimEnd('\')
target = $target
contents = $contents
}

return $persist_def
}

function persist_helper($source, $target, $contents = $null, $method = "link", $encoding = "ASCII") {
write-host "Persisting $source"

$source = fullpath "$dir\$source"
$target = fullpath "$persist_dir\$target"

# if we have had persist data in the store, just create link and go
if (Test-Path $target) {
# if there is also a source data, using $method to determine what to do
if (Test-Path $source) {
if (is_directory $source) {
# for dir persisting
switch ($method) {
# keep $source
"copy" {
Remove-Item $target -Recurse -Force
movedir $source $target
}
# keep all files based on $target
"merge" { movedir $source $target "/XC /XN /XO" }
# keep all newer files
"update" { movedir $source $target "/XO" }
# keep $target ("link")
Default { movedir $source "$source.original" }
}
} else {
# target is a file, create hard link
& "$env:COMSPEC" /c "mklink /h `"$source`" `"$target`"" | out-null
# for file persisting
switch ($method) {
# keep $source
"copy" { Move-Item $source $target -Force }
# keep newer
"update" {
if ((Get-Item $source).LastWriteTimeUtc -gt (Get-Item $target).LastWriteTimeUtc){
Move-Item $source $target -Force
} else {
Rename-Item $source "$source.original" -Force
}
}
# keep $target ("link", "merge")
Default { Rename-Item $source "$source.original" -Force }
}
}
}
# we don't have persist data in the store, move the source to target, then create link
} elseif (Test-Path $source) {
# ensure target parent folder exist
$null = ensure (Split-Path -Path $target)
Move-Item $source $target
# use file contents to determine $source's type, $null for directory and others for file
} elseif ($null -eq $contents) {
New-Item $target -ItemType Directory -Force | Out-Null
} else {
$null = ensure (Split-Path -Path $target)
$contents = $ExecutionContext.InvokeCommand.ExpandString($contents)
Out-File -FilePath $target -Encoding $encoding -InputObject $contents -Force
}
}

# create link
if (is_directory $target) {
# target is a directory, create junction
create_junction $source $target | Out-Null
} else {
# target is a file, create hard link
create_hardlink $source $target | Out-Null
}
}
function unlink_persist_data($dir) {
# unlink all junction / hard link in the directory
Get-ChildItem -Recurse $dir | ForEach-Object {
$file = $_
if ($null -ne $file.LinkType) {
$filepath = $file.FullName
# directory (junction)
if ($file -is [System.IO.DirectoryInfo]) {
# remove read-only attribute on the link
attrib -R /L $filepath
# remove the junction
& "$env:COMSPEC" /c "rmdir /s /q $filepath"
} else {
# remove the hard link
& "$env:COMSPEC" /c "del $filepath"
}
Remove-Item $file.FullName -Recurse -Force
}
}
}
Expand Down
Loading