Repository: spack/spack

Issue:
Cannot extract archive, unrecognized file extension: 'None'
### Steps to reproduce

On linux,

```
$ spack -d stage eccodes@2.25.0
==> [2022-07-08-17:43:03.348355] Staging archive: /tmp/harmen/spack-stage/spack-stage-eccodes-2.25.0-svaf535zwdloqoikjuskycdctfqnns7t/eccodes-2.25.0-Source.tar.gz?api=v2
==> [2022-07-08-17:43:03.348504] CommandNotFoundError: Cannot extract archive, unrecognized file extension: 'None'
==> [2022-07-08-17:43:03.348532] Error: Cannot extract archive, unrecognized file extension: 'None'
Traceback (most recent call last):
  File "/home/harmen/spack/lib/spack/spack/main.py", line 893, in main
    return _main(argv)
  File "/home/harmen/spack/lib/spack/spack/main.py", line 848, in _main
    return finish_parse_and_run(parser, cmd_name, env_format_error)
  File "/home/harmen/spack/lib/spack/spack/main.py", line 876, in finish_parse_and_run
    return _invoke_command(command, parser, args, unknown)
  File "/home/harmen/spack/lib/spack/spack/main.py", line 533, in _invoke_command
    return_val = command(parser, args)
  File "/home/harmen/spack/lib/spack/spack/cmd/stage.py", line 64, in stage
    package.do_stage()
  File "/home/harmen/spack/lib/spack/spack/package_base.py", line 1577, in do_stage
    self.stage.expand_archive()
  File "/home/harmen/spack/lib/spack/spack/util/pattern.py", line 18, in __call__
    return [getattr(item, self.name)(*args, **kwargs)
  File "/home/harmen/spack/lib/spack/spack/util/pattern.py", line 18, in <listcomp>
    return [getattr(item, self.name)(*args, **kwargs)
  File "/home/harmen/spack/lib/spack/spack/stage.py", line 610, in expand_archive
    self.fetcher.expand()
  File "/home/harmen/spack/lib/spack/spack/fetch_strategy.py", line 84, in wrapper
    return fun(self, *args, **kwargs)
  File "/home/harmen/spack/lib/spack/spack/fetch_strategy.py", line 539, in expand
    decompress = decompressor_for(self.archive_file, self.extension)
  File "/home/harmen/spack/lib/spack/spack/util/compression.py", line 258, in decompressor_for
    raise CommandNotFoundError("Cannot extract archive, \
spack.util.executable.CommandNotFoundError: Cannot extract archive, unrecognized file extension: 'None'

```

@scheibelp @johnwparent yet another instance where there's regression on Linux thanks to Windows support. Please fix... it's such a waste of time :(



Discussion:
Apart from restoring the pre-Windows behavior, there's multiple better solutions.

First, the original filename is part of the response header and can be parsed from there.

```
$ curl -i https://confluence.ecmwf.int/download/attachments/45757960/eccodes-2.25.0-Source.tar.gz?api=v2
HTTP/1.1 200 200
....
Content-Disposition: inline; filename="eccodes-2.25.0-Source.tar.gz"
```

In CURL this is:

```
$ curl -JO https://confluence.ecmwf.int/download/attachments/45757960/eccodes-2.25.0-Source.tar.gz?api=v2
curl: Saved to filename 'eccodes-2.25.0-Source.tar.gz'
```

This also solves the special case code for sourceforge...

```
$ curl -LJO https://sourceforge.net/projects/argtable/files/argtable/argtable-2.13/argtable2-13.tar.gz/download
$ ls
argtable2-13.tar.gz
```

Secondly, the best option is to actually parse the file magic bytes to figure out what type of archive it is regardless of the url, or response headers, which should obviously be much more reliable...
Thanks for pointing this out @haampie, but regarding

> yet another instance where there's regression on Linux thanks to Windows support. Please fix... it's such a waste of time :(

I'm having trouble extracting anything useful from that: we want Windows support; adding features creates bugs; we catch as many as we can with unit/smoke tests. Besides improving the tests and responding to issues as they are brought up, what else is there to do?
Sorry, I was frustrated with Spack... I tried parallel `spack install` and hit 5 unique bugs. You'd think that a decade old project that is supposed to install software can install software.

Resolving patch:
diff --git a/lib/spack/spack/fetch_strategy.py b/lib/spack/spack/fetch_strategy.py
--- a/lib/spack/spack/fetch_strategy.py
+++ b/lib/spack/spack/fetch_strategy.py
@@ -530,7 +530,7 @@ def expand(self):
                 "Failed on expand() for URL %s" % self.url)
 
         if not self.extension:
-            self.extension = extension(self.url)
+            self.extension = extension(self.archive_file)
 
         if self.stage.expanded:
             tty.debug('Source already staged to %s' % self.stage.source_path)
diff --git a/lib/spack/spack/stage.py b/lib/spack/spack/stage.py
--- a/lib/spack/spack/stage.py
+++ b/lib/spack/spack/stage.py
@@ -366,9 +366,9 @@ def expected_archive_files(self):
         fnames = []
         expanded = True
         if isinstance(self.default_fetcher, fs.URLFetchStrategy):
+            url_path = url_util.parse(self.default_fetcher.url).path
             expanded = self.default_fetcher.expand_archive
-            clean_url = os.path.basename(
-                sup.sanitize_file_path(self.default_fetcher.url))
+            clean_url = os.path.basename(sup.sanitize_file_path(url_path))
             fnames.append(clean_url)
 
         if self.mirror_paths:


Test patch: