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

Add version variables to autoupdate.hash.regex #2996

Conversation

niheaven
Copy link
Member

Add version variables to autoupdate.hash.regex for some hash extract situations.

The idea is from Atom's nupkg hash extraction. Atom's releases page provides nupkg named atom-1.34.0-full.nupkg (for i686) and atom-x64-1.34.0-full.nupkg (for x64_86), and it also provides RELEASES and RELEASES-x64 files that give sha1 hash of nupkg files. Unfortunately in RELEASES-x64 the nupkg file name is atom-1.34.0-full.nupkg without -x64.

I haven't found some method to write a regex to extract the hash without $version, but get_hash_for_app() only support `$basename$ in regex. So I add the code, and it works for the following hash extract:

"url": "https://github.com/atom/atom/releases/download/v$version/atom-x64-$version-full.nupkg#dl.7z",
"hash": {
    "url": "$baseurl/RELEASES-x64",
    "regex": "([a-fA-F0-9]+)\\s+?(?:atom-$version-full.nupkg)"
}

@r15ch13
Copy link
Member

r15ch13 commented Jan 16, 2019

How about adding a $regexEscape parameter to substitute:

function substitute($entity, [Hashtable] $params, [Bool]$regexEscape = $false) {
    if ($entity -is [Array]) {
        return $entity | ForEach-Object { substitute $_ $params $regexEscape}
    } elseif ($entity -is [String]) {
        $params.GetEnumerator() | ForEach-Object {
            if($regexEscape -eq $false -or $null -eq $_.Value) {
                $entity = $entity.Replace($_.Name, $_.Value)
            } else {
                $entity = $entity.Replace($_.Name, [Regex]::Escape($_.Value))
            }
        }
        return $entity
    }
}

And instead of using $basename-Parameter for find_hash_in_textfile() we pass down the $substitutions. (requires adding the $basename to the hashtable)

diff --git a/lib/autoupdate.ps1 b/lib/autoupdate.ps1
index 5b77e759..b14e2d4e 100644
--- a/lib/autoupdate.ps1
+++ b/lib/autoupdate.ps1
@@ -29,7 +29,7 @@ function find_hash_in_rdf([String] $url, [String] $filename) {
     return format_hash $digest.sha256
 }
 
-function find_hash_in_textfile([String] $url, [String] $basename, [String] $regex) {
+function find_hash_in_textfile([String] $url, [Hashtable] $substitutions, [String] $regex = '^([a-fA-F0-9]+)$') {
     $hashfile = $null
 
     try {
@@ -43,15 +43,8 @@ function find_hash_in_textfile([String] $url, [String] $basename, [String] $rege
         return
     }
 
-    # find single line hash in $hashfile (will be overridden by $regex)
-    if ($regex.Length -eq 0) {
-        $normalRegex = "^([a-fA-F0-9]+)$"
-    } else {
-        $normalRegex = $regex
-    }
-
-    $normalRegex = substitute $normalRegex @{'$basename' = [regex]::Escape($basename)}
-    if ($hashfile -match $normalRegex) {
+    $regex = substitute $regex $substitutions $true
+    if ($hashfile -match $regex) {
         $hash = $matches[1] -replace ' ',''
     }
 
@@ -70,7 +63,7 @@ function find_hash_in_textfile([String] $url, [String] $basename, [String] $rege
     # find hash with filename in $hashfile (will be overridden by $regex)
     if ($hash.Length -eq 0 -and $regex.Length -eq 0) {
         $filenameRegex = "([a-fA-F0-9]{32,128})[\x20\t]+.*`$basename(?:[\x20\t]+\d+)?"
-        $filenameRegex = substitute $filenameRegex @{'$basename' = [regex]::Escape($basename)}
+        $filenameRegex = substitute $filenameRegex $substitutions $true
         if ($hashfile -match $filenameRegex) {
             $hash = $matches[1]
         }
@@ -142,12 +135,12 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
     $hashmode = $config.mode
     $basename = url_remote_filename($url)
 
-    $hashfile_url = substitute $config.url @{
-        '$url' = (strip_fragment $url);
-        '$baseurl' = (strip_filename (strip_fragment $url)).TrimEnd('/')
-        '$basename' = $basename
-    }
-    $hashfile_url = substitute $hashfile_url $substitutions
+    $substitutions = $substitutions.Clone()
+    $substitutions.Add('$url', (strip_fragment $url))
+    $substitutions.Add('$baseurl', (strip_filename (strip_fragment $url)).TrimEnd('/'))
+    $substitutions.Add('$basename', $basename)
+
+    $hashfile_url = substitute $config.url $substitutions
     if($hashfile_url) {
         write-host -f DarkYellow 'Searching hash for ' -NoNewline
         write-host -f Green $(url_remote_filename $url) -NoNewline
@@ -176,22 +169,16 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
         $regex = $config.regex
     }
 
-    $substitutions.GetEnumerator() | ForEach-Object {
-        if ($_.Value) {
-            $regex = $regex.Replace($_.Name, [regex]::Escape($_.Value))
-        }
-    }
-
     if (!$hashfile_url -and $url -match "(?:downloads\.)?sourceforge.net\/projects?\/(?<project>[^\/]+)\/(?:files\/)?(?<file>.*)") {
         $hashmode = 'sourceforge'
         # change the URL because downloads.sourceforge.net doesn't have checksums
         $hashfile_url = (strip_filename (strip_fragment "https://sourceforge.net/projects/$($matches['project'])/files/$($matches['file'])")).TrimEnd('/')
-        $hash = find_hash_in_textfile $hashfile_url $basename '"$basename":.*?"sha1":\s"([a-fA-F0-9]{40})"'
+        $hash = find_hash_in_textfile $hashfile_url $substitutions '"$basename":.*?"sha1":\s"([a-fA-F0-9]{40})"'
     }
 
     switch ($hashmode) {
         'extract' {
-            $hash = find_hash_in_textfile $hashfile_url $basename $regex
+            $hash = find_hash_in_textfile $hashfile_url $substitutions $regex
         }
         'json' {
             $hash = find_hash_in_json $hashfile_url $basename $jsonpath
@@ -202,7 +189,7 @@ function get_hash_for_app([String] $app, $config, [String] $version, [String] $u
         'metalink' {
             $hash = find_hash_in_headers $url
             if(!$hash) {
-                $hash = find_hash_in_textfile "$url.meta4"
+                $hash = find_hash_in_textfile "$url.meta4" $substitutions
             }
         }
     }
diff --git a/lib/core.ps1 b/lib/core.ps1
index 2205ce08..25980198 100644
--- a/lib/core.ps1
+++ b/lib/core.ps1
@@ -612,12 +612,16 @@ function is_scoop_outdated() {
     return $last_update.AddHours(3) -lt $now.ToLocalTime()
 }
 
-function substitute($entity, [Hashtable] $params) {
+function substitute($entity, [Hashtable] $params, [Bool]$regexEscape = $false) {
     if ($entity -is [Array]) {
-        return $entity | ForEach-Object { substitute $_ $params }
+        return $entity | ForEach-Object { substitute $_ $params $regexEscape}
     } elseif ($entity -is [String]) {
         $params.GetEnumerator() | ForEach-Object {
-            $entity = $entity.Replace($_.Name, $_.Value)
+            if($regexEscape -eq $false -or $null -eq $_.Value) {
+                $entity = $entity.Replace($_.Name, $_.Value)
+            } else {
+                $entity = $entity.Replace($_.Name, [Regex]::Escape($_.Value))
+            }
         }
         return $entity
     }

@niheaven
Copy link
Member Author

Thanks @r15ch13, I've just made a minimal change to achieve my goal, and your implement are more systemic and readable. I've adopted your advise and extended it to JSONPath (though may not needed now).

I've tested the code on atom's nupkg version (for regex), nuget (for jsonpath checkver) and openssl (for jsonpath hash extraction), everything is okay, the new logic works well and the old logic is not affected.

It's good for me now, and if merged, I'll submit a pr about new hash extract method of atom.

@r15ch13 r15ch13 merged commit 3a22526 into ScoopInstaller:master Jan 17, 2019
@niheaven niheaven deleted the add-version-variables-to-regex-in-hash-extract branch January 21, 2019 01:52
r15ch13 pushed a commit that referenced this pull request Jan 21, 2019
Fix a bug introduced in #2996 that single line hash extraction is broken.

When `find_hash_in_textfile()` is called in `get_hash_for_app()`, `$null` is used as `$regex` value for default single line hash extraction and the default value (`'^([a-fA-F0-9]+)$'`) is overriden, which leads a error of "hash not found".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants