laravel-zero/phar-updater

一个使PHAR自更新变得简单和安全的工具。

v1.4.0 2023-09-01 10:40 UTC

This package is auto-updated.

Last update: 2024-09-18 16:35:54 UTC


README

Latest Version on Packagist Software License Build Status Static Analysis Total Downloads

这是一个为Laravel Zero内部使用而复制的Humbug PHAR Updater。

Laravel Zero PHAR自更新命令的后端。最初由Humbug创建。

目录

介绍

laravel-zero/phar-updater包具有以下特性

  • 完全支持SSL/TLS验证。
  • 支持OpenSSL phar签名。
  • 简单的API,要么更新,要么异常会失控。
  • 支持SHA-1/SHA-256/SHA-512版本同步和Github发布作为更新策略。

除了以下详细文档,您还可以在Laravel Zero的自更新组件中找到该包的使用。

安装

通过Composer

composer require laravel-zero/phar-updater

通过Laravel Zero组件安装器

php <application> app:install self-update

该包使用PHP Streams进行远程请求,因此需要启用openssl扩展和allow_url_open设置。curl的支持将在以后跟进。

使用

默认更新策略使用当前远程phar的SHA-1哈希版本文件,并在版本更改时更新本地phar。还有一个Github策略,它跟踪Github发布,您可以在发布中上传新的phar文件。

基本的SHA-1 / SHA-256 / SHA-512策略

注意:SHA-1策略已被标记为弃用,您应该选择SHA-256或SHA-512策略。

创建您的自更新命令,甚至为其他phar(不是当前的phar)创建更新命令,并包含此内容。

/**
 * The simplest usage assumes the currently running phar is to be updated and
 * that it has been signed with a private key (using OpenSSL).
 *
 * The first constructor parameter is the path to a phar if you are not updating
 * the currently running phar.
 */

use Humbug\SelfUpdate\Updater;

$updater = new Updater();

// Add the below to use the SHA-256 strategy. It will default to SHA-1 if excluded.
$updater->setStrategy(Updater::STRATEGY_SHA256);

$updater->getStrategy()->setPharUrl('https://example.com/current.phar');
$updater->getStrategy()->setVersionUrl('https://example.com/current.version');
try {
    $result = $updater->update();
    echo $result ? "Updated!\n" : "No update needed!\n";
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

如果您不使用OpenSSL签名phar

/**
 * The second parameter to the constructor must be false if your phars are
 * not signed using OpenSSL.
 */

use Humbug\SelfUpdate\Updater;

$updater = new Updater(null, false);
$updater->getStrategy()->setPharUrl('https://example.com/current.phar');
$updater->getStrategy()->setVersionUrl('https://example.com/current.version');
try {
    $result = $updater->update();
    echo $result ? "Updated!\n" : "No update needed!\n";
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

如果您需要版本信息

use Humbug\SelfUpdate\Updater;

$updater = new Updater();
$updater->getStrategy()->setPharUrl('https://example.com/current.phar');
$updater->getStrategy()->setVersionUrl('https://example.com/current.version');
try {
    $result = $updater->update();
    if ($result) {
        $new = $updater->getNewVersion();
        $old = $updater->getOldVersion();
        printf(
            'Updated from SHA-1 %s to SHA-1 %s', $old, $new
        );
    } else {
        echo "No update needed!\n";
    }
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

请参阅更新策略部分,了解如何设置SHA-1或SHA-256策略的概述。它是一个简单的维护选择,用于开发或夜间版本的phars,这些phars被发布到特定的版本号。

Github发布策略

除了开发或夜间phars之外,如果您在Github上发布数字版本(即标签),您可以将其他文件(如phars)上传到Github发布中。

/**
 * Other than somewhat different setters for the strategy, all other operations
 * are identical.
 */

use Humbug\SelfUpdate\Updater;

$updater = new Updater();
$updater->setStrategy(Updater::STRATEGY_GITHUB);
$updater->getStrategy()->setPackageName('myvendor/myapp');
$updater->getStrategy()->setPharName('myapp.phar');
$updater->getStrategy()->setCurrentLocalVersion('v1.0.1');
try {
    $result = $updater->update();
    echo $result ? "Updated!\n" : "No update needed!\n";
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

包名称是指Packagist使用的名称,而phar名称是假定为跨版本恒定的phar文件名。

实现者负责提供与本地phar关联的当前发布版本。这需要在phar中存储,并且应与Github使用的版本字符串匹配。这可以遵循任何可识别的前缀和后缀的标准实践,例如v1.0.31.0.31.11.3rc1.3.2pl2

如果您想更新到非稳定版本,例如,用户想根据开发分支更新,您可以为Github策略设置稳定性标志。默认情况下,它设置为stable或常量形式\Humbug\SelfUpdate\Strategy\GithubStrategy::STABLE

$updater->getStrategy()->setStability('unstable');

如果您想忽略稳定性并更新到最新版本

$updater->getStrategy()->setStability('any');

回滚支持

更新器会自动将原始phar的备份复制到myname-old.phar。您可以使用此约定非常容易地触发回滚

use Humbug\SelfUpdate\Updater;

/**
 * Same constructor parameters as you would use for updating. Here, just defaults.
 */
$updater = new Updater();
try {
    $result = $updater->rollback();
    if (!$result) {
        echo "Failure!\n";
        exit(1);
    }
    echo "Success!\n";
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

由于用户在命名和定位备份方面可能有不同的需求,您可以使用setBackupPath()函数在更新当前phar时显式管理备份应写入的确切路径,或在使用setRestorePath()触发回滚之前读取路径。这些将取代简单内置的约定。

构造函数参数

更新器的构造函数相当简单。三种基本变化是

/**
 * Default: Update currently running phar which has been signed.
 */
$updater = new Updater;
/**
 * Update currently running phar which has NOT been signed.
 */
$updater = new Updater(null, false);
/**
 * Use a strategy other than the default SHA Hash.
 */
$updater = new Updater(null, false, Updater::STRATEGY_GITHUB);
/**
 * Update a different phar which has NOT been signed.
 */
$updater = new Updater('/path/to/impersonatephil.phar', false);

检查更新

您可以通过使用hasUpdate方法来告诉用户在任何稳定性轨迹上可用的更新。这将获取某个稳定性级别的最新远程版本,将其与当前版本进行比较,并返回简单的真/假结果,即仅在本地版本相同或该稳定性级别根本不存在远程版本时为假。您可以很容易地区分这两种假状态,因为新版本将是一个字符串,其中版本确实存在,但如果不存在则为false

use Humbug\SelfUpdate\Updater;

/**
 * Configuration is identical in every way for actual updates. You can run this
 * across multiple configuration variants to get recent stable, unstable, and dev
 * versions available.
 *
 * This would configure update for an unsigned phar (second constructor must be
 * false in this case).
 */
$updater = new Updater(null, false);
$updater->setStrategy(Updater::STRATEGY_GITHUB);
$updater->getStrategy()->setPackageName('myvendor/myapp');
$updater->getStrategy()->setPharName('myapp.phar');
$updater->getStrategy()->setCurrentLocalVersion('v1.0.1');

try {
    $result = $updater->hasUpdate();
    if ($result) {
        printf(
            'The current stable build available remotely is: %s',
            $updater->getNewVersion()
        );
    } elseif (false === $updater->getNewVersion()) {
        echo "There are no stable builds available.\n";
    } else {
        echo "You have the current stable build installed.\n";
    }
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

避免更新后的文件包含

更新正在运行的phar变得更为复杂,因为一旦替换,尝试从来自较旧phar的进程加载文件可能会创建一个phar内部损坏错误。例如,如果您正在使用Symfony Console并为您自己的命令创建事件分发器,一些事件类的延迟加载将会有这种影响。

解决方案是禁用或删除您自更新命令的调度器。

一般来说,当编写您的自更新CLI命令时,要么在更新之前预先加载可能需要的任何类,要么如果它们不是必需的,则禁用它们的加载。

自定义更新策略

所有更新策略都围绕检查更新和下载更新展开。替换本地文件和备份的实际工作由其他部分处理。要创建自定义策略,您可以实现Humbug\SelfUpdate\Strategy\StrategyInterface并在构造后传递您实现的新的实例。

$updater = new Updater(null, false);
$updater->setStrategyObject(new MyStrategy);

类似的setStrategy()方法仅用于传递匹配内部策略的标志。

更新策略

SHA-1 / SHA-256 / SHA-512哈希同步

phar-updater包支持一种更新策略,其中phar根据远程可用的当前PHAR文件的SHA-1、SHA-256或SHA-512哈希值进行更新。这假设只有两个到三个远程文件存在

  • myname.phar
  • myname.version
  • myname.phar.pubkey(可选)

myname.phar是最最近构建的phar。

myname.version包含最最近构建的phar的SHA-1、SHA-256或SHA-512哈希值,其中哈希值是文件中的第一个字符串(如果不是唯一的字符串)。您可以使用bash生成它

# For SHA-1
sha1sum myname.phar > myname.version

# For SHA-256
sha256sum myname.phar > myname.version

# For SHA-512
sha512sum myname.phar > myname.version

请记住,对于您要分发的每个新phar构建,都要重新生成版本文件。使用sha1sum/sha256sum/sha512sum在哈希值后添加附加数据,但这没关系,因为哈希值是文件中的第一个字符串,这是唯一的要求。

如果您使用非常推荐的OpenSSL签名,您也可以将公钥放在线作为myname.phar.pubkey,用于您的phar的初始安装。但是,请注意,phar-updater本身将永远不会下载此密钥,也永远不会在您的文件系统中替换此密钥,也永远不会安装一个无法由本地缓存的公钥验证签名的phar。

如果您需要出于任何原因切换密钥,用户将需要手动下载包含新密钥的新phar。虽然这听起来很极端,但通常不鼓励在不让用户知道的情况下允许任意密钥更改。OpenSSL签名没有类似于中央权威机构或浏览器信任证书存储的机制,可以以安全的方式自动化此类密钥更改。

Github发布

在Github上标记新版本时,这些版本将被创建并托管为Github Releases,允许您附加变更日志和额外的文件下载。利用这个Github功能,您可以附加新的phar文件到版本中,并将其与在Packagist上发布的版本字符串关联。

利用这种架构,更新phar文件的Github策略可以比较现有本地phar的版本与远程发布版本,并更新到最新稳定(或不稳定)版本。

目前,假设phar文件在所有版本中名称都相同,即文件名仅包含一个普通名称,如myapp.phar,而不包含版本信息。您也可以以同样的方式上传您的phar公钥。使用已建立的惯例,即phar名称后附加.pubkey,例如myapp.phar将与myapp.phar.pubkey匹配。

您可以在这里了解更多关于Github发布的信息。

虽然您可以草拟一个发布版本,但每当您创建一个新的git标签时,Github发布版本都会自动创建。如果您使用git标签,您可以转到匹配的发布版本,点击编辑按钮并附加文件。建议在标记后尽快这样做,以限制新发布存在但未附加更新phar的时间窗口。

直接下载

PHAR Updater提供了一个抽象的Humbug\SelfUpdate\Strategy\DirectDownloadStrategyAbstract,可以用于通过仅使用getDownloadUrl(): string方法快速轻松地创建下载策略。

例如,如果PHAR从https://example.com/latest/example.phar下载最新更新,您可以使用以下代码来实现这一点

use Humbug\SelfUpdate\Strategy\DirectDownloadStrategyAbstract;

class ExampleDirectDownloadStrategy extends DirectDownloadStrategyAbstract
{
    public function getDownloadUrl(): string
    {
        return 'https://example.com/latest/example.phar';
    }
}

这个抽象策略还支持覆盖getCurrentRemoteVersion()方法,因此您可以添加自定义HTTP调用或其他方法来查看最新版本。默认情况下,它返回字符串latest

use Illuminate\Support\Facades\Http;
use Humbug\SelfUpdate\Strategy\DirectDownloadStrategyAbstract;

class ExampleDirectDownloadStrategy extends DirectDownloadStrategyAbstract
{
    /** {@inheritdoc} */
    public function getCurrentRemoteVersion(Updater $updater)
    {
        return Http::get('https://example.com/example-releases.json')->object()->latest_version;
    }

    public function getDownloadUrl(): string
    {
        return "https://example.com/{$this->getCurrentRemoteVersion()}/example.phar";
    }
}

您还可以使用setCurrentLocalVersion()getCurrentLocalVersion()方法设置和检索当前本地版本,这些方法将用于与远程版本进行比较。