Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README-es.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ Cuando no se especifica `type`, DLoad automáticamente usa todos los manejadores
> Usar `type="archive"` extraerá incluso archivos `.phar`.

> [!NOTE]
> Cuando hay un `<binary>` configurado, con `type="binary"` o sin `type`, los paquetes del sistema operativo (`.deb`, `.rpm`, `.apk`, `.snap`, `.flatpak`, `.msi`, `.msix`, `.msixbundle`, `.appx`, `.appxbundle`, `.nupkg`, `.dmg`, `.pkg`) nunca se seleccionan: DLoad no puede desempaquetarlos. Un release que solo ofrece un paquete para el host se omite en favor de uno más antiguo, entre los 10 releases más recientes que cumplen la restricción de versión; fija `version` para ir más atrás.
> Cuando hay un `<binary>` configurado, con `type="binary"` o sin `type`, los paquetes del sistema operativo (`.deb`, `.rpm`, `.apk`, `.snap`, `.flatpak`, `.msi`, `.msix`, `.msixbundle`, `.appx`, `.appxbundle`, `.nupkg`, `.dmg`, `.pkg`) nunca se seleccionan: DLoad no puede desempaquetarlos. Tampoco los archivos comprimidos en un formato que DLoad no puede desempaquetar: los formatos de archivo comprimido conocidos distintos de `.zip`, `.tar.gz`, `.tgz`, `.tar`, `.gz` y `.phar`, como `.tar.xz`, `.tar.bz2`, `.zst`, `.7z` o `.rar`; un archivo sin una extensión de archivo comprimido conocida, como un binario simple o un `.AppImage`, sí se selecciona. Un release que solo ofrece estos assets para el host se omite en favor de uno más antiguo, entre los 10 releases más recientes que cumplen la restricción de versión; fija `version` para ir más atrás.

#### Extracción de Archivos (Preservando la Estructura)

Expand Down
2 changes: 1 addition & 1 deletion README-ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ DLoad поддерживает три типа загрузки, которые
> Использование `type="archive"` распакует даже `.phar` архивы.

> [!NOTE]
> Если задан `<binary>` — с `type="binary"` или без `type`, — пакеты ОС (`.deb`, `.rpm`, `.apk`, `.snap`, `.flatpak`, `.msi`, `.msix`, `.msixbundle`, `.appx`, `.appxbundle`, `.nupkg`, `.dmg`, `.pkg`) никогда не выбираются: DLoad не умеет их распаковывать. Релиз, в котором для хоста есть только пакет, пропускается ради более старого — среди 10 новейших релизов, удовлетворяющих ограничению версии; чтобы уйти дальше в прошлое, закрепите `version`.
> Если задан `<binary>` — с `type="binary"` или без `type`, — пакеты ОС (`.deb`, `.rpm`, `.apk`, `.snap`, `.flatpak`, `.msi`, `.msix`, `.msixbundle`, `.appx`, `.appxbundle`, `.nupkg`, `.dmg`, `.pkg`) никогда не выбираются: DLoad не умеет их распаковывать. Не выбираются и архивы форматов, которые DLoad не распаковывает, — известные форматы архивов, кроме `.zip`, `.tar.gz`, `.tgz`, `.tar`, `.gz` и `.phar`, например `.tar.xz`, `.tar.bz2`, `.zst`, `.7z` или `.rar`; файл без известного расширения архива, такой как обычный бинарник или `.AppImage`, по-прежнему выбирается. Релиз, в котором для хоста есть только такие ассеты, пропускается ради более старого — среди 10 новейших релизов, удовлетворяющих ограничению версии; чтобы уйти дальше в прошлое, закрепите `version`.

#### Распаковка архива с сохранением структуры

Expand Down
2 changes: 1 addition & 1 deletion README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ DLoad 支持三种下载类型,它们决定了资源的处理方式:
> 使用 `type="archive"` 会解包甚至 `.phar` 压缩包。

> [!NOTE]
> 配置了 `<binary>` 时(无论是 `type="binary"` 还是未指定 `type`),永远不会选择操作系统安装包(`.deb`、`.rpm`、`.apk`、`.snap`、`.flatpak`、`.msi`、`.msix`、`.msixbundle`、`.appx`、`.appxbundle`、`.nupkg`、`.dmg`、`.pkg`):DLoad 无法解包它们。如果某个发布版本只为当前主机提供安装包,则会跳过它,改用更早的版本,范围限于满足版本约束的最新 10 个发布版本;要回溯更早的版本,请固定 `version`。
> 配置了 `<binary>` 时(无论是 `type="binary"` 还是未指定 `type`),永远不会选择操作系统安装包(`.deb`、`.rpm`、`.apk`、`.snap`、`.flatpak`、`.msi`、`.msix`、`.msixbundle`、`.appx`、`.appxbundle`、`.nupkg`、`.dmg`、`.pkg`):DLoad 无法解包它们。DLoad 无法解包的格式的压缩包同样不会被选择——即 `.zip`、`.tar.gz`、`.tgz`、`.tar`、`.gz` 和 `.phar` 以外的已知压缩格式,例如 `.tar.xz`、`.tar.bz2`、`.zst`、`.7z` 或 `.rar`;没有已知压缩包扩展名的文件(例如普通二进制文件或 `.AppImage`)仍会被选择。如果某个发布版本只为当前主机提供这类资源,则会跳过它,改用更早的版本,范围限于满足版本约束的最新 10 个发布版本;要回溯更早的版本,请固定 `version`。

#### 解包压缩包(保留结构)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ When `type` is not specified, DLoad automatically uses all available handlers:
> Using `type="archive"` will unpack even `.phar` archives.

> [!NOTE]
> When a `<binary>` is configured, with `type="binary"` or without a `type`, OS packages (`.deb`, `.rpm`, `.apk`, `.snap`, `.flatpak`, `.msi`, `.msix`, `.msixbundle`, `.appx`, `.appxbundle`, `.nupkg`, `.dmg`, `.pkg`) are never selected: DLoad cannot unpack them. A release that offers only a package for the host is skipped for an older one, among the 10 newest releases that satisfy the version constraint; pin `version` to go further back.
> When a `<binary>` is configured, with `type="binary"` or without a `type`, OS packages (`.deb`, `.rpm`, `.apk`, `.snap`, `.flatpak`, `.msi`, `.msix`, `.msixbundle`, `.appx`, `.appxbundle`, `.nupkg`, `.dmg`, `.pkg`) are never selected: DLoad cannot unpack them. Neither are archives of a format DLoad cannot unpack — known archive formats other than `.zip`, `.tar.gz`, `.tgz`, `.tar`, `.gz` and `.phar`, such as `.tar.xz`, `.tar.bz2`, `.zst`, `.7z` or `.rar`; a file without a known archive extension, like a plain binary or an `.AppImage`, is still selected. A release that offers only such assets for the host is skipped for an older one, among the 10 newest releases that satisfy the version constraint; pin `version` to go further back.

#### Archive Extraction (Preserving Structure)

Expand Down
2 changes: 1 addition & 1 deletion skills/dload-fetch-tool/references/registry-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Filters the release's asset list. dload then runs OS/arch detection on every mat
- match all OS/arch variants of the tool;
- leave out sibling tools, checksums, signatures, source archives.

Build variants stay in the pattern too. Among the assets for the host, dload prefers the host libc (`musl` builds on Alpine and Android, the others elsewhere), then the name with the fewest extra tokens — so `bun-linux-x64.zip` wins over `-baseline`, `-profile` and `-debug` twins — then archives. Android builds are dropped on Linux whenever the entry has a `binary` (without one, every other platform is only ranked lower), and so are OS packages (`.deb`, `.rpm`, `.apk`, `.snap`, `.flatpak`, `.msi`, `.msix`, `.msixbundle`, `.appx`, `.appxbundle`, `.nupkg`, `.dmg`, `.pkg`) everywhere: dload cannot unpack them, so a release that has only a package for the host gives way to an older one among the 10 newest releases that satisfy `version` (pin `version` to go further back). On ARM macOS and Windows, x86-64 builds are a fallback: Rosetta 2 and the Windows emulation run them.
Build variants stay in the pattern too. Among the assets for the host, dload prefers the host libc (`musl` builds on Alpine and Android, the others elsewhere), then the name with the fewest extra tokens — so `bun-linux-x64.zip` wins over `-baseline`, `-profile` and `-debug` twins — then archives. Android builds are dropped on Linux whenever the entry has a `binary` (without one, every other platform is only ranked lower), and so are OS packages (`.deb`, `.rpm`, `.apk`, `.snap`, `.flatpak`, `.msi`, `.msix`, `.msixbundle`, `.appx`, `.appxbundle`, `.nupkg`, `.dmg`, `.pkg`) and archives dload has no extractor for (known archive formats other than `.zip`, `.tar.gz`, `.tgz`, `.tar`, `.gz`, `.phar` — such as `.tar.xz`, `.tar.bz2`, `.zst`, `.pkg.tar.zst`, `.7z`, `.rar`) everywhere: dload cannot unpack them, so a release that has only such assets for the host gives way to an older one among the 10 newest releases that satisfy `version` (pin `version` to go further back). On ARM macOS and Windows, x86-64 builds are a fallback: Rosetta 2 and the Windows emulation run them.

Tokens the OS/arch matchers recognise (case-insensitive, bounded by `_` or a word boundary):

Expand Down
2 changes: 1 addition & 1 deletion skills/dload-fetch-tool/references/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ A candidate that matches neither is discarded. Common offenders:

Compare with the host: `php -r "echo PHP_OS_FAMILY, ' / ', php_uname('m'), PHP_EOL;"`.

With a `binary`, OS packages (`.deb`, `.msi`… — the list is in [`registry-entry.md`](registry-entry.md#asset-pattern)) are discarded too, since dload cannot unpack them, and a release offering only packages is skipped for an older one among the 10 newest releases that satisfy `version`, with a warning naming both versions. When the package names carry the host OS and arch, the report says "only OS packages match" and lists them under "OS packages are skipped because a binary is expected"; a package without an OS token (`tool.dmg`) is discarded by the OS check above instead. When no older release fits, pin a `version` that ships an archive or a raw binary for the host — pinning also reaches past the 10 newest.
With a `binary`, OS packages (`.deb`, `.msi`… — the list is in [`registry-entry.md`](registry-entry.md#asset-pattern)) and archives of a format dload cannot unpack (known archive formats other than `.zip`, `.tar.gz`, `.tgz`, `.tar`, `.gz`, `.phar`, such as `.tar.xz`, `.7z`, `.zst`) are discarded too, and a release offering only such assets is skipped for an older one among the 10 newest releases that satisfy `version`, with a warning naming both versions. When their names carry the host OS and arch, the report says "only OS packages match" or "only archives of an unsupported format match" and lists them under "OS packages are skipped because a binary is expected" or "Archives of an unsupported format are skipped because a binary is expected"; one without an OS token (`tool.dmg`) is discarded by the OS check above instead. When no older release fits, pin a `version` that ships a supported archive or a raw binary for the host — pinning also reaches past the 10 newest.

Several candidates left: dload tries them best first — host libc, then the fewest extra name tokens, then archives, then the release order. Run with `-vvv` to see the rank of every asset (`Asset ... ranked: os=0 arch=0 libc=0 extras=1 archive=0`); a wrong pick usually means the wanted build carries an extra token the others lack.

Expand Down
11 changes: 11 additions & 0 deletions src/Module/Archive/ArchiveFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,22 @@ private function bootDefaultMatchers(): void
static fn(\SplFileInfo $info): Archive => new GzArchive($info),
), ['gz']);

// Registered after `gz`: the matchers added last are tried first.
$this->extend($this->matcher(
'tar.gz',
static fn(\SplFileInfo $info): Archive => new TarPharArchive($info),
), ['tar.gz']);

$this->extend($this->matcher(
'tgz',
static fn(\SplFileInfo $info): Archive => new TarPharArchive($info),
), ['tgz']);

$this->extend($this->matcher(
'tar',
static fn(\SplFileInfo $info): Archive => new TarPharArchive($info),
), ['tar']);

$this->extend($this->matcher(
'phar',
static fn(\SplFileInfo $info): Archive => new PharArchive($info),
Expand Down
4 changes: 2 additions & 2 deletions src/Module/Archive/Internal/TarPharArchive.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Internal\DLoad\Module\Archive\Internal;

/**
* Archive handler for TAR.GZ archives
* Archive handler for TAR archives, plain or gzipped (`.tar`, `.tar.gz`, `.tgz`)
*
* ```php
* // Extracting from TAR.GZ archive
Expand All @@ -21,7 +21,7 @@
final class TarPharArchive extends PharAwareArchive
{
/**
* Opens TAR.GZ archive for reading
* Opens TAR archive for reading
*
* @param \SplFileInfo $file Archive file
*/
Expand Down
37 changes: 28 additions & 9 deletions src/Module/Downloader/Downloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Candidate;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Rule\ArchitectureRule;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Rule\PackageRule;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Rule\UnsupportedArchiveRule;
use Internal\DLoad\Module\Downloader\Internal\DownloadContext;
use Internal\DLoad\Module\Downloader\Task\DownloadResult;
use Internal\DLoad\Module\Downloader\Task\DownloadTask;
Expand Down Expand Up @@ -179,6 +180,15 @@ public function download(
);
}

/**
* @param list<Candidate> $candidates
* @return list<non-empty-string>
*/
private static function assetNames(array $candidates): array
{
return \array_map(static fn(Candidate $candidate): string => $candidate->asset->getName(), $candidates);
}

/**
* Creates the repository of the current config.
*
Expand Down Expand Up @@ -214,7 +224,8 @@ private function createRepository(DownloadContext $context): ?Repository
private function processRepository(Repository $repository, DownloadContext $context, bool $mayRetry = true): \Closure
{
return function () use ($repository, $context, $mayRetry): ReleaseInterface {
$context->packagesOnlyRelease = null;
$context->skippedRelease = null;
$context->skippedReleaseAssets = '';
// Set when a release turned out to be deleted: the release list is outdated then
$forgotten = false;

Expand Down Expand Up @@ -273,11 +284,12 @@ private function processRepository(Repository $repository, DownloadContext $cont
await(coroutine($this->processRelease($context)));

/** @var ReleaseInterface|null $skipped */
$skipped = $context->packagesOnlyRelease;
$skipped = $context->skippedRelease;
$skipped === null or $this->logger->warning(
'`%s` %s has only OS packages for `%s`/`%s`, downloaded %s instead.',
'`%s` %s has only %s for `%s`/`%s`, downloaded %s instead.',
$context->software->getId(),
$skipped->getVersion()->string,
$context->skippedReleaseAssets,
$this->operatingSystem->value,
$this->architecture->value,
$context->release->getVersion()->string,
Expand Down Expand Up @@ -349,18 +361,25 @@ private function processRelease(DownloadContext $context): \Closure
);
$this->logger->debug('%d matching assets found.', \count($selection->candidates));

$skippedPackages = \array_map(
static fn(Candidate $candidate): string => $candidate->asset->getName(),
$selection->removed[PackageRule::KEY] ?? [],
);
$skippedPackages = self::assetNames($selection->removed[PackageRule::KEY] ?? []);
$skippedArchives = self::assetNames($selection->removed[UnsupportedArchiveRule::KEY] ?? []);
$context->releaseAttempt->registerSkippedPackages($skippedPackages);
$context->releaseAttempt->registerSkippedArchives($skippedArchives);

$skipped = \implode(' and ', [
...($skippedPackages === [] ? [] : ['OS packages']),
...($skippedArchives === [] ? [] : ['archives of an unsupported format']),
]);
if ($selection->isEmpty() && $skipped !== '' && $context->skippedRelease === null) {
$context->skippedRelease = $context->release;
$context->skippedReleaseAssets = $skipped;
}

$selection->isEmpty() && $skippedPackages !== [] and $context->packagesOnlyRelease ??= $context->release;
$selection->isEmpty() and throw new NotFound(
$strict
? \sprintf(
'%s OS `%s`, architecture `%s`, name pattern `%s`%s',
$skippedPackages === [] ? 'no asset matches' : 'only OS packages match',
$skipped === '' ? 'no asset matches' : "only {$skipped} match",
$this->operatingSystem->value,
$this->architecture->value,
$context->repoConfig->assetPattern,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Rule\NamePatternRule;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Rule\OperatingSystemRule;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Rule\PackageRule;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Rule\UnsupportedArchiveRule;
use Internal\DLoad\Module\Repository\AssetInterface;
use Psr\Container\ContainerInterface;

Expand Down Expand Up @@ -55,9 +56,10 @@ public function __construct(
new CompanionRule(),
new OperatingSystemRule($operatingSystem),
new ArchitectureRule($architecture, $operatingSystem),
// After the platform rules, so the report names only packages built for the host. A package whose
// name carries no OS token, like `tool.dmg`, is already gone then and stays out of the report.
// After the platform rules, so the report names only packages and archives built for the host. One whose
// name carries no OS token, like `tool.dmg` or `tool.7z`, is gone by then and stays out of the report.
new PackageRule(),
new UnsupportedArchiveRule($archiveFactory),
new LibcRule($container),
new ExtrasRule(),
new ArchiveRule($archiveFactory),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Internal\DLoad\Module\Downloader\Internal\AssetSelection\Rule;

use Internal\DLoad\Module\Archive\ArchiveFactory;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\AssetRule;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Candidate;
use Internal\DLoad\Module\Downloader\Internal\AssetSelection\Selection;

/**
* Removes archives dload has no extractor for (`.tar.xz`, `.7z`, `.zst`…) when a binary is expected.
*
* Like an OS package, see {@see PackageRule}, such an archive downloads fine and then fails
* the installation after the release loop is over. Removed here, a release with only such
* an archive for the host has no matching asset, and the downloader moves on to the next one.
*
* Only the known archive formats are removed: a name with another extension or none is taken
* for a raw binary.
*
* @internal
* @psalm-internal Internal\DLoad\Module\Downloader
*/
final class UnsupportedArchiveRule implements AssetRule
{
/** Reason the archives are removed under, see {@see Selection::$removed}. */
public const KEY = 'unsupported-archive';

/**
* Archive and compression formats, the supported ones included: the formats an extractor
* is added for stop being removed without a change here.
*/
private const EXTENSIONS = [
'zip', 'gz', 'tgz', 'tar', 'xz', 'txz', 'bz2', 'tbz', 'tbz2',
'zst', 'tzst', 'lz', 'lzma', 'lz4', '7z', 'rar',
];

public function __construct(
private readonly ArchiveFactory $archiveFactory,
) {}

public function select(Selection $selection, callable $next): Selection
{
$supported = $this->archiveFactory->getSupportedExtensions();

$selection->strict and $selection = $selection->remove(
static fn(Candidate $candidate): bool => $candidate->hasExtension(self::EXTENSIONS)
&& !$candidate->hasExtension($supported),
self::KEY,
);

return $next($selection);
}
}
Loading
Loading