3 Commits

Author SHA1 Message Date
Richard Kuhnt
c2f1e8eeb1 Add new 'persist'-object representation to schema.json
- Deprecates the String representation
- Deprecates the StringArray representation
- Introduces first version of Object representation
2019-03-12 20:43:33 +01:00
Richard Kuhnt
2bcf54422a Create backwards compatible persist_data_old() function
- Extract old persist_data() functionality for backwards compatible with old manifests
- Introduce new persist_data_new() stub
2019-03-12 20:39:09 +01:00
Richard Kuhnt
2f850502e8 Add functions for junction and hardlink handling including Tests
- Add is_junction()
- Add create_junction()
- Add remove_junction()
- Add create_hardlink()
- Add remove_hardlink()
2019-03-12 20:36:43 +01:00
4 changed files with 269 additions and 65 deletions

View File

@@ -285,7 +285,47 @@ 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 is_junction([String] $path) {
return (Test-Path $path) -and (Get-Item -Path $path).LinkType -eq 'Junction'
}
function create_junction([String] $link, [String] $target) {
if (!(Test-Path $link) -and (is_directory $target)) {
New-Item -ItemType Junction -Path (Split-Path -Path $link) -Name (Split-Path -Leaf $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 remove_junction([String] $link) {
if(is_junction $link) {
$dirInfo = New-Object System.IO.DirectoryInfo($link)
$dirInfo.Attributes = $dirInfo.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
$dirInfo.Delete()
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 (Split-Path -Path $link) -Name (Split-Path -Leaf $link) -Target $target | Out-Null
return $true
}
return $false
}
function remove_hardlink([String] $link) {
if ((Test-Path $link) -and !(is_directory $link)) {
Remove-Item -Path $link | Out-Null
return $true
}
return $false
}
function movedir($from, $to) {

View File

@@ -927,14 +927,9 @@ 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
}
& "$env:COMSPEC" /c mklink /j $currentdir $versiondir | out-null
attrib $currentdir +R /L
# recreate new junction
remove_junction $currentdir | Out-Null
create_junction $currentdir $versiondir | Out-Null
return $currentdir
}
@@ -950,11 +945,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_junction $currentdir | Out-Null
return $currentdir
}
return $versiondir
@@ -1144,56 +1135,76 @@ function persist_def($persist) {
function persist_data($manifest, $original_dir, $persist_dir) {
$persist = $manifest.persist
if($persist) {
$persist_dir = ensure $persist_dir
if(!$persist) {
return
}
$persist_dir = ensure $persist_dir
if ($persist -is [String]) {
$persist = @($persist);
}
if ($persist -is [String]) {
persist_data_old $persist $original_dir $persist_dir
return
}
if ($persist -is [Array]) {
$persist | ForEach-Object {
$source, $target = persist_def $_
write-host "Persisting $source"
$source = $source.TrimEnd("/").TrimEnd("\\")
$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, 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($_ -is [Object]) {
persist_data_new $_ $original_dir $persist_dir
}
# 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
} else {
# target is a file, create hard link
& "$env:COMSPEC" /c "mklink /h `"$source`" `"$target`"" | out-null
if ($_ -is [String]) {
persist_data_old $_ $original_dir $persist_dir
}
}
}
}
function persist_data_new($persist, $original_dir, $persist_dir) {
# TODO
}
function persist_data_old($persist, $original_dir, $persist_dir) {
if(!$persist) {
return
}
$source, $target = persist_def $persist
write-host "Persisting $source"
$source = $source.TrimEnd("/").TrimEnd("\\")
$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, 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
}
# 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 {
@@ -1202,13 +1213,9 @@ function unlink_persist_data($dir) {
$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"
remove_junction | Out-Null
} else {
# remove the hard link
& "$env:COMSPEC" /c "del $filepath"
remove_hardlink $filepath | Out-Null
}
}
}

View File

@@ -202,6 +202,80 @@
},
"type": "object"
},
"persist": {
"anyOf": [
{
"description": "Deprecated, use object representation instead.",
"type": "string"
},
{
"description": "Deprecated, use object representation instead.",
"items": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"minItems": 1,
"type": "array"
},
{
"minItems": 1,
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"type"
],
"if": {
"properties": {
"type": {
"const": "file"
}
}
},
"then": {
"required": [
"encoding",
"content"
]
},
"properties": {
"name": {
"type": "string"
},
"target": {
"type": "string"
},
"type": {
"enum": [
"file",
"directory",
"folder"
]
},
"encoding": {
"enum": [
"utf-8",
"ascii",
"byte"
]
},
"content": {
"$ref": "#/definitions/stringOrArrayOfStrings"
},
"method": {
"enum": [
"copy",
"merge",
"link",
"update"
]
}
}
}
}
]
},
"checkver": {
"anyOf": [
{
@@ -407,7 +481,7 @@
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
},
"persist": {
"$ref": "#/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings"
"$ref": "#/definitions/persist"
},
"checkver": {
"$ref": "#/definitions/checkver"

View File

@@ -6,21 +6,104 @@
$repo_dir = (Get-Item $MyInvocation.MyCommand.Path).directory.parent.FullName
$isUnix = is_unix
describe "is_directory" -Tag 'Scoop' {
describe "directory/junction/hardlink handling" -Tag 'Scoop' {
beforeall {
$working_dir = setup_working "is_directory"
}
it "is_directory recognize directories" {
it "is_directory recognizes directories" {
is_directory "$working_dir\i_am_a_directory" | should -be $true
}
it "is_directory recognize files" {
it "is_directory recognizes files" {
is_directory "$working_dir\i_am_a_file.txt" | should -be $false
}
it "is_directory is falsey on unknown path" {
is_directory "$working_dir\i_do_not_exist" | should -be $false
}
it "create_junction recognizes directories and creates junction" {
create_junction "$working_dir\i_am_a_junction" "$working_dir\i_am_a_directory" | should -be $true
Test-Path "$working_dir\i_am_a_junction" | should -be $true
}
it "create_junction recognizes existing target" {
create_junction "$working_dir\i_am_a_junction" "$working_dir\i_am_a_directory" | should -be $false
}
it "create_junction recognizes files" {
create_junction "$working_dir\i_am_a_junction_file.txt" "$working_dir\i_am_a_file.txt" | should -be $false
Test-Path "$working_dir\i_am_a_junction_file.txt" | should -be $false
}
it "create_junction is falsey on unknown path" {
create_junction "$working_dir\i_am_a_junction" "$working_dir\i_do_not_exist" | should -be $false
}
it "is_junction recognizes junctions" {
is_junction "$working_dir\i_am_a_junction" | should -be $true
}
it "is_junction recognizes directories" {
is_junction "$working_dir\i_am_a_directory" | should -be $false
}
it "is_junction recognizes files" {
is_junction "$working_dir\i_am_a_file.txt" | should -be $false
}
it "is_junction is falsey on unknown path" {
is_junction "$working_dir\i_do_not_exist" | should -be $false
}
it "remove_junction recognizes junctions" {
remove_junction "$working_dir\i_am_a_junction" | should -be $true
Test-Path "$working_dir\i_am_a_junction" | should -be $false
}
it "remove_junction recognizes directories" {
remove_junction "$working_dir\i_am_a_directory" | should -be $false
}
it "remove_junction recognizes files" {
remove_junction "$working_dir\i_am_a_file.txt" | should -be $false
}
it "remove_junction is falsey on unknown path" {
remove_junction "$working_dir\i_do_not_exist" | should -be $false
}
it "create_hardlink recognizes files and creates hardlink" {
create_hardlink "$working_dir\i_am_a_hardlinked_file.txt" "$working_dir\i_am_a_file.txt" | should -be $true
Test-Path "$working_dir\i_am_a_hardlinked_file.txt" | should -be $true
}
it "create_hardlink recognizes existing target" {
create_hardlink "$working_dir\i_am_a_hardlinked_file.txt" "$working_dir\i_am_a_file.txt" | should -be $false
}
it "create_hardlink recognizes directories" {
create_hardlink "$working_dir\i_am_a_hardlinked_directory" "$working_dir\i_am_a_directory" | should -be $false
Test-Path "$working_dir\i_am_a_hardlinked_directory" | should -be $false
}
it "create_hardlink is falsey on unknown path" {
create_hardlink "$working_dir\i_do_not_exist.txt" "$working_dir\i_do_not_exist.txt" | should -be $false
}
it "remove_hardlink recognizes hardlinks/files" {
remove_hardlink "$working_dir\i_am_a_hardlinked_file.txt" | should -be $true
Test-Path "$working_dir\i_am_a_hardlinked_file.txt" | should -be $false
}
it "remove_hardlink recognizes directories" {
remove_hardlink "$working_dir\i_am_a_directory" | should -be $false
}
it "remove_hardlink is falsey on unknown path" {
remove_hardlink "$working_dir\i_do_not_exist" | should -be $false
}
}
describe "movedir" -Tag 'Scoop' {