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

[WIP] Persistence rework with backward compatibility (persist) #3212

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

r15ch13
Copy link
Member

@r15ch13 r15ch13 commented Mar 12, 2019

Reference discussions: #2897 #3209

Complete rework of the persist property including backward compatibility.

  • Introduces new persist-objects to JSON Schema subject to change
      "persist": [
          {
              "type": "file",
              "name": "config.dat",
              "content": "[Config]`nlanguage=eng",
              "encoding": "UTF-8",
              "method": "copy"
          },
          {
              "type": "directory",
              "name": "data",
              "method": "link"
          },
          {
              "type": "directory",
              "name": "plugins",
              "method": "merge"
          }
      ]
    • method
      • merge: Merge new Languages/Plugins/Skins directory with current persistent directory to update those but don't lose changes made by the user
      • copy: just copy directory or file (maybe the same as merge)
      • link: create hardlink for files or junctions for directories
    • encoding: file encoding (not used for directories)
    • type: either file or directory
    • content: either string or array of strings
    • glue: characters to join content array (default \n)
  • Introduces new functions for Junction and Hardlink handling including Tests
    • Drops $env:COMSPEC and attrib calls
  • Extract old persist_data behaviour to persist_data_old subject to change
  • More tests (more mocking)
  • Implement actual feature 😁

Cases that need to be adressed:

  • support files in subdirectory
  • support subdirectories
  • Installation:
    • file exists
      • copy file to persist directory and hardlink it
    • file doesn't exist
      • create file from content and hardlink it
    • directory doesn't exist
      • create symlink (link)
    • directory exists and is empty
      • remove directory and create symlink (link)
    • directory exists and isn't empty
      • rename directory to <name>.original?
  • Before Update:
    • file exists
      • copy file to persist directory
    • directory exists and is not empty
      • copy content over to persistent directory (merge)
  • After Update:
    • file exists
      • rename file to <filename>.original to keep changes
      • copy old user file back and hardlink it
    • file doens't exist
      • copy old user file back and hardlink it
    • directory exists and is empty
      • remove directory and create symlink (link)
    • directory exists and is not empty
      • rename directory to <name>.original?
      • copy content over to persistent directory (merge)

- Add is_junction()
- Add create_junction()
- Add remove_junction()
- Add create_hardlink()
- Add remove_hardlink()
- Extract old persist_data() functionality for backwards compatible with old manifests
- Introduce new persist_data_new() stub
- Deprecates the String representation
- Deprecates the StringArray representation
- Introduces first version of Object representation
"enum": [
"file",
"directory",
"folder"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why both folder and directory? Is it just alias or some actual differences?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, might drop it. Only directory should be fine.

@Ash258
Copy link
Contributor

Ash258 commented Mar 12, 2019

Small notes without code review:

Make encoding UTF8 encoding default in code (eg. When not specified, use UTF-8), same for method link.


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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is needless to seperate Path and Name (refer to New-Item doc

"required": [
"encoding",
"content"
]
Copy link
Member

@niheaven niheaven Mar 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two properties should not be required, they could be ommited in most instances with no contents and encoding "UTF-8" (which is default in Windows 10 in despite of encoding with Default, ASCII, OEM in Out-File, but "UTF8" give "UTF-8 with BOM"...).

If app require different encoding, we can set through Get-Content | Out-File -Encoding, but it should be rare case.

{
"description": "Deprecated, use object representation instead.",
"items": {
"$ref": "#/definitions/stringOrArrayOfStrings"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have #/definitions/stringOrArrayOfStringsOrAnArrayOfArrayOfStrings, why not using it as former?

if(is_junction $link) {
$dirInfo = New-Object System.IO.DirectoryInfo($link)
$dirInfo.Attributes = $dirInfo.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
$dirInfo.Delete()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove-Item xxx -Recurse -Force could remove read-only junction. So if we want to delete junction or hardlink, a single function that Remove-Item xxx -Recurse -Force is enough.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I got an error message saying powershell is not in interactive mode, so I changed it to a simple delete be the object is already there.

Sent from my OnePlus5T using FastHub

Copy link
Member

@niheaven niheaven Mar 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested with changing two remove_ function's contents to Remove-Item (after fix typo in L1216), and also with replacing all remove_junction and remove_hardlink with Remove-Item xxx -Recurse -Force | Out-Null (with a file existing check in link_current), and gotten no error. Maybe need more test.

PS. I've googled that without -Force may return the error that not in interactive mode. May help.

attrib -R /L $filepath
# remove the junction
& "$env:COMSPEC" /c "rmdir /s /q $filepath"
remove_junction | Out-Null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small typo: missing $filename

if(is_junction $link) {
$dirInfo = New-Object System.IO.DirectoryInfo($link)
$dirInfo.Attributes = $dirInfo.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
$dirInfo.Delete()
Copy link
Member

@niheaven niheaven Mar 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested with changing two remove_ function's contents to Remove-Item (after fix typo in L1216), and also with replacing all remove_junction and remove_hardlink with Remove-Item xxx -Recurse -Force | Out-Null (with a file existing check in link_current), and gotten no error. Maybe need more test.

PS. I've googled that without -Force may return the error that not in interactive mode. May help.

@rasa
Copy link
Member

rasa commented Mar 13, 2019

Can most values default, so

  "persist": [
      {
          "name": "config.dat" // type=file, method=link
      },
      {
          "name": "data/" // type=directory, method=link
      }
  ]

works?

@niheaven
Copy link
Member

Yes, it works, use a regex to judge if it contains "/" or "", as a shorcut of "type".

@Ash258
Copy link
Contributor

Ash258 commented Mar 13, 2019

No need to use regex. Just "dir/".EndsWith('/') (-or "dir\\".EndsWith('\\') for people like me, who prefer windows like separator) should be enough.

@niheaven
Copy link
Member

Remark for encoding: Out-File in PowerShell 3-5.1 use "ASCII" as default, and "UTF8" give a UTF-8 with BOM. The same function in PowerShell Core (6) use "UTF8NoBOM" as default, and "UTF8" give a UTF-8 without BOM. So PLEASE AVOID using "UTF8" as file contents encoding. "ASCII" is preferred.

@chawyehsu
Copy link
Member

chawyehsu commented Mar 13, 2019

AFAIK previously, for restricted users, read-only junctions can interrupt uninstallation, and can not be removed by Remove-Item -r -force(#2832 #2881 #2882 #2890 ). Maybe need more tests under non-admin accounts.

@niheaven
Copy link
Member

@h404bi Agree. I've tested with an admin account (with and without elevated privilege) and a non-admin test acount, and didn't gotten error with Remove-Item -Recurse -Force. This may need more tests.

@Ash258
Copy link
Contributor

Ash258 commented May 4, 2019

Before Update:
file exists
copy file to persist directory

This is to prevent file overwriting instead of file editing? (mainly some qT apps ScoopInstaller/Extras#2018)

If yes i would suggest this tweak to description:

-Before update
+Before uninstallation

niheaven added a commit to niheaven/scoop that referenced this pull request May 13, 2019
niheaven added a commit to niheaven/scoop that referenced this pull request May 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants