Skip to content

Smarter auto-updating re. PHP requirements #6004

Closed

Description

@schlessera

Right now, the WP-CLI default updating mechanism will look at whether the latest available release is newer than the currently installed one and offer to update to that new version (in interactive mode or when triggered via wp cli update).

This creates a problem when we raise the PHP minimum version. Let's say we bump the PHP version to 7.0+ with WP-CLI 3.0.0. On a system running on PHP 5.6, it will see that 3.0.0 is available and propose to update. When the user confirms this, the update will just break and it will revert to the already installed version. But the notice will not go away to install 3.0.0 (or any subsequent version), as WP-CLI does not check the PHP compatibility for available updates.

Another problem is that, if you are running on 2.12.0 and you cannot use 3.0.0, you can also not easily get security updates. If we'd push a 2.12.1 security hotfix, the updater would still only see 3.0.0 and try to update to that.

I suggest two separate changes:

  1. always update to the latest non-major first before trying to update a major. So, in the above case, the updater would first update to 2.9.1, before attempting to update to 3.0.0. This will ensure that we have ways of reaching all major versions with security patches or updater fixes.

  2. think about how best to add PHP requirements into the updater process so that the update is not even being considered when it is known to not be compatible. You should still be able to override this, of course, as the PHP version you are running for updates is not necessarily the same you use to run WP-CLI against a given site. But the default behavior should be safe enough to never fatal out or get stuck in update notifications you cannot get rid of anymore.

Activity

  1. added this to the 2.12.0 milestone on Oct 16, 2024
  2. swissspidy commented on Jan 10, 2025

    @swissspidy
    Member

    Another problem is that, if you are running on 2.12.0 and you cannot use 3.0.0, you can also not easily get security updates. If we'd push a 2.12.1 security hotfix, the updater would still only see 3.0.0 and try to update to that.

    If we do (2) though, then you would not see 3.0.0 because of the PHP version incompatibility.

    Also, one can still use wp cli update --patch.

    2. think about how best to add PHP requirements into the updater process so that the update is not even being considered when it is known to not be compatible. You should still be able to override this, of course, as the PHP version you are running for updates is not necessarily the same you use to run WP-CLI against a given site. But the default behavior should be safe enough to never fatal out or get stuck in update notifications you cannot get rid of anymore.

    Right now this is how it works:

    If you explicitly call wp cli update --nightly or wp-cli update --stable, we hardcode the phar path:

    if ( Utils\get_flag_value( $assoc_args, 'nightly' ) ) {
    WP_CLI::confirm( sprintf( 'You have version %s. Would you like to update to the latest nightly?', WP_CLI_VERSION ), $assoc_args );
    $download_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar';
    $md5_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar.md5';
    } elseif ( Utils\get_flag_value( $assoc_args, 'stable' ) ) {
    WP_CLI::confirm( sprintf( 'You have version %s. Would you like to update to the latest stable release?', WP_CLI_VERSION ), $assoc_args );
    $download_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar';
    $md5_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar.md5';
    } else {

    If you don't provide anything, we call the releases API and loop through all releases to find the latest major/minor/patch version:

    private function get_updates( $assoc_args ) {
    $url = 'https://api.github.com/repos/wp-cli/wp-cli/releases?per_page=100';
    $options = [
    'timeout' => 30,
    'insecure' => (bool) Utils\get_flag_value( $assoc_args, 'insecure', false ),
    ];
    $headers = [
    'Accept' => 'application/json',
    ];
    $github_token = getenv( 'GITHUB_TOKEN' );
    if ( false !== $github_token ) {
    $headers['Authorization'] = 'token ' . $github_token;
    }
    $response = Utils\http_request( 'GET', $url, null, $headers, $options );
    if ( ! $response->success || 200 !== $response->status_code ) {
    WP_CLI::error( sprintf( 'Failed to get latest version (HTTP code %d).', $response->status_code ) );
    }
    $release_data = json_decode( $response->body );
    $updates = [
    'major' => false,
    'minor' => false,
    'patch' => false,
    ];
    foreach ( $release_data as $release ) {
    // Get rid of leading "v" if there is one set.
    $release_version = $release->tag_name;
    if ( 'v' === substr( $release_version, 0, 1 ) ) {
    $release_version = ltrim( $release_version, 'v' );
    }
    $update_type = Utils\get_named_sem_ver( $release_version, WP_CLI_VERSION );
    if ( ! $update_type ) {
    continue;
    }
    if ( ! isset( $release->assets[0]->browser_download_url ) ) {
    continue;
    }
    if ( ! empty( $updates[ $update_type ] ) && ! Comparator::greaterThan( $release_version, $updates[ $update_type ]['version'] ) ) {
    continue;
    }
    $updates[ $update_type ] = [
    'version' => $release_version,
    'update_type' => $update_type,
    'package_url' => $release->assets[0]->browser_download_url,
    ];
    }
    foreach ( $updates as $type => $value ) {
    if ( empty( $value ) ) {
    unset( $updates[ $type ] );
    }
    }
    foreach ( [ 'major', 'minor', 'patch' ] as $type ) {
    if ( true === Utils\get_flag_value( $assoc_args, $type ) ) {
    return ! empty( $updates[ $type ] ) ? [ $updates[ $type ] ] : false;
    }
    }
    if ( empty( $updates ) && preg_match( '#-alpha-(.+)$#', WP_CLI_VERSION, $matches ) ) {
    $version_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/NIGHTLY_VERSION';
    $response = Utils\http_request( 'GET', $version_url, null, [], $options );
    if ( ! $response->success || 200 !== $response->status_code ) {
    WP_CLI::error( sprintf( 'Failed to get current nightly version (HTTP code %d)', $response->status_code ) );
    }
    $nightly_version = trim( $response->body );
    if ( WP_CLI_VERSION !== $nightly_version ) {
    $updates['nightly'] = [
    'version' => $nightly_version,
    'update_type' => 'nightly',
    'package_url' => 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar',
    ];
    }
    }
    return array_values( $updates );
    }

    If you explicitly provide --major/--minor/--patch, then a matching release will be found (if one exists that is newer)

    Regarding the PHP requirements:

    There is no way to add such metadata to GitHub releases. Thus, we must find another way.

    We could add a simple JSON file to https://github.com/wp-cli/builds that contains the latest releases and additional metadata like PHP version requirements. The file could be updated automatically during the release process, details TBD.

    Alternatively, if we don't like a static JSON file, we could add a dynamic updates API endpoint to wp-cli.org, which would serve the right update based on the version you passed (similar to the WP updates API endpoint). But I don't like moving away from GitHub just for that.

    Then, WP-CLI would fetch that JSON file or API endpoint to figure out which release to pick.

  3. swissspidy commented on Jan 11, 2025

    @swissspidy
    Member

    There is no way to add such metadata to GitHub releases. Thus, we must find another way.

    Actually, we could upload some metadata.json field as a release asset... Release creation is currently somewhat manual, we basically just upload the files from https://github.com/wp-cli/builds there. So as part of the build process we could have such a metadata.json file as well. This file could actually just live in the https://github.com/wp-cli/wp-cli root and be maintained & updated there, and just copied to the builds repo during the process. From there, it just has to be attached to the GitHub release.

    Then, \CLI_Command::get_updates() just has to be a bit smarter by going through all releases and checking that manifest file (if it exists) to check for the PHP compatibility (and potentially other stuff).


    Related: we should probably check SHA512 hashes first and only falling back to MD5 in the process.

  4. phanduynam commented on Apr 3, 2025

    @phanduynam

    Does no one have a solution to this problem?

  5. swissspidy commented on Apr 3, 2025

    @swissspidy
    Member

    IDK what you mean, this is actually resolved :-)

  6. phanduynam commented on Apr 3, 2025

    @phanduynam

    @swissspidy I mean it's been a while and the issue is still not resolved even though it seems to be done, if it is done then close it now to release version 2.12.0, I'm still waiting.

  7. swissspidy commented on Apr 3, 2025

    @swissspidy
    Member

    It is resolved and hence I closed it :-) 2.12 will be released soon.

  8. phanduynam commented on Apr 3, 2025

    @phanduynam

    @swissspidy there are several other issues that I see are resolved but not closed:
    #5918
    #5962
    #5987
    #6013
    #6040

  9. swissspidy commented on Apr 3, 2025

    @swissspidy
    Member

    These are simply pull requests that still need to be finalized and reviewed and merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions