Problem/Motivation

Here I'd like to test out some theories that using a quasi-patch zip file will work update things in place.

This currently depends on a new test type in #3031379: Add a new test type to do real update testing.
The only available update paths at this point (9/16/2019) are the ones listed in #3061248: Package quasi-patch for automatic updates. This will probably change to encompass more projects sometime soon.

Proposed resolution

Write some code

Remaining tasks

Do it.

CommentFileSizeAuthor
#57 3055872-57.patch35.47 KBheddn
#57 interdiff_56-57.txt510 bytesheddn
#56 3055872-56.patch35.47 KBheddn
#56 interdiff_54-56.txt1.14 KBheddn
#54 interdiff_53_54.txt594 bytesheddn
#54 3055872-54.patch35.35 KBheddn
#53 3055872-53.patch35.34 KBheddn
#53 interdiff_52-53.txt1.94 KBheddn
#52 interdiff_50-52.txt10.93 KBheddn
#52 3055872-52.patch36.18 KBheddn
#50 interdiff_49-50.txt456 bytesheddn
#50 3055872-50.patch32.78 KBheddn
#49 3055872-47.patch32.75 KBheddn
#48 interdiff_43-47.txt5.45 KBheddn
#48 interdiff_43-47.txt5.45 KBheddn
#43 3055872-43.patch32.16 KBheddn
#43 interdiff_40-43.txt10.61 KBheddn
#40 interdiff_39-40.txt4.48 KBheddn
#40 3055872-40.patch29.11 KBheddn
#39 3055872-39.patch393.04 KBheddn
#39 interdiff_38-39.txt5.23 KBheddn
#38 interdiff_37-38.txt178 bytesheddn
#38 3055872-38.patch393.76 KBheddn
#37 3055872-36.patch393.76 KBheddn
#36 3055872-36.patch31.31 KBheddn
#36 interdiff_34-36.txt4.87 KBheddn
#34 interdiff_33-34.txt7.3 KBheddn
#34 3055872-34.patch393.11 KBheddn
#33 3055872-33.patch328.56 KBheddn
#31 3055872-31.patch328.12 KBheddn
#31 interdiff_30-31.patch15.48 KBheddn
#30 3055872-30.patch523.86 KBheddn
#29 3055872-29.patch23.75 KBheddn
#28 interdiff_26-28.txt1.31 KBheddn
#28 3055872-28.PATCH23.75 KBheddn
#26 3055872-25.patch523.22 KBheddn
#26 interdiff_24-25.txt1.81 KBheddn
#25 3055872-25.patch523.39 KBheddn
#24 3055872-23.patch23.28 KBheddn
#24 interdiff_19-23.txt13.11 KBheddn
#19 3055872-19.patch87.07 KBheddn
#15 interdiff_13-15.txt496 bytesheddn
#15 3055872-15.patch87 KBheddn
#13 3055872-13.patch87 KBheddn
#13 interdiff_10-13.txt4.34 KBheddn
#10 3055872-10.patch86.88 KBheddn
#10 interdiff_9-10.txt26.15 KBheddn
#9 3055872-8.patch83.85 KBheddn
#4 3055872-4.patch81.42 KBheddn
#4 interdiff_3-4.txt3.71 KBheddn
#3 3055872-3.patch81.45 KBheddn
#3 interdiff_2-3.txt8.06 KBheddn
#2 3055872.patch18.44 KBheddn
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

heddn created an issue. See original summary.

heddn’s picture

Status: Active » Needs review
FileSize
18.44 KB

Here's an initial PoC of what an in-place update would look like. This is simple at this point and manually tested. What do we think? It allows updating from 8.7.0 => 8.7.1. Sorta. The majority of that release is in the vendor folder. Which I didn't bundle. I only bundled the parts that are directly part of core for this PoC. But it demonstrates we'll need a system that does update the vendor folder. Not just the core folder.

heddn’s picture

FileSize
8.06 KB
81.45 KB

OK, here's a more complete picture of how things might work. Still need to incorporate the deletions.

heddn’s picture

phpcs fixes only.

catch’s picture

Not done a full review, but in general the 'get a zip, extract, copy the individual files over' seems quite vindicated by the patch, really doesn't look too bad so far.

heddn’s picture

re #5: that feedback is really helpful. I had a similar reaction after looking at everything. There's some edge cases. We still need to account for deletions and rollbacks. But over-all, things are in a pretty good state.

Testing this whole thing is going to be complicated. I'm hopeful that #3031379: Add a new test type to do real update testing lands sometime soon to make this more possible.

catch’s picture

+++ b/src/Services/Update.php
@@ -0,0 +1,301 @@
+  protected function getFilesList($directory) {

For deletions, does this need to use a manifest?

heddn’s picture

re #7: Yes, I think so. @mbaynton commented that's how he's doing it today and I was going to incorporate that feature when I pick this up again.

heddn’s picture

This is manually tested and seems to work for the whole thing. Including deletions. Lots of code review and there might be edge cases that are unaccounted. But this shows that with a fairly small amount of code, in place updates can occur.

Next to work on tests.

Sorry there's no interdiff. It needed a re-roll and I didn't post that as a patch first.

heddn’s picture

Super basic test. Deletions happen last in the update process. So if the file is gone, we assume that the update process got that far. For a PoC, it is enough.

heddn’s picture

Good green. I'd like to keep this focused on the service and its API (interactions) with an FTP site that hosts the files.zip files. In follow-ups, we'll wire the service into a (manual) button and (automated) cron to apply the updates.

catch’s picture

Only nitpicks, general approach looks good.

With the manifest do we definitely want this only for deletions, or for the included files too would be my only proper question.

  1. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,385 @@
    +   * InPlaceUpdate constructor.
    

    I think this should be 'Constructs an InPlaceUpdate'?

  2. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,385 @@
    +    $sink = $this->fileSystem->realpath($this->fileSystem->getDestinationFilename("temporary://$project_name.zip", FileSystemInterface::EXISTS_RENAME));
    

    Why sink for the variable name?

  3. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,385 @@
    +   * Backup before an update.
    

    The comment says backup but the method says process.

  4. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,385 @@
    +        return FALSE;
    

    Should this have a watchdog_exception()?

  5. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,385 @@
    +        return FALSE;
    

    Same here.

  6. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,385 @@
    +   * Backup before a backup.
    

    Backup before an update?

  7. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,385 @@
    +    $this->backup = $this->fileSystem->createFilename('automatic_updates-backup', 'temporary://');
    +    $this->fileSystem->prepareDirectory($this->backup);
    +    $this->backup = $this->fileSystem->realpath($this->backup) . DIRECTORY_SEPARATOR;
    

    Should this just assign to $backup first then $this->backup after the realpath call?

heddn’s picture

FileSize
4.34 KB
87 KB

re #12
0.5 (manifest): I had a similar thought at one point and eventually rejected it. Too troublesome to parse the manifest file for deletions vs creations, vs updates. Much easier to just delete all files listed in the file. Plus I'm not sure what the benefit (except completeness) for adding all the files.
1: fixed comment
2: renamed variable
3: fixed comment
4-5: the filesystem service already logs it, I don't think there is a need to double log.
6: fixed comment
7: fixed variable assignment

+++ b/src/Services/UpdateInterface.php
@@ -0,0 +1,29 @@
+  const DELETION_MANIFEST = 'deleted_files.txt';

I also renamed constant value to DELETION_MANIFEST.txt

Status: Needs review » Needs work

The last submitted patch, 13: 3055872-13.patch, failed testing. View results

heddn’s picture

heddn’s picture

Status: Needs work » Needs review
catch’s picture

This is looking really solid to me. Is there an issue open to figure out actually creating the zip releases?

heddn’s picture

FileSize
87.07 KB

Minor re-roll

Status: Needs review » Needs work

The last submitted patch, 19: 3055872-19.patch, failed testing. View results

greg.1.anderson’s picture

There have been some concerns expressed that the work here is not compatible with what's happening in the Composer in Core initiative, so I wanted to drill down on how the zip files are being generated.

First, a couple of definitions.

The Composer in Core initiative is going to deliver "Composer-ready" tarball downloads. That means that if you download Drupal and untar it, you can immediately cd into your directory and run composer update or composer require, and the right thing will happen.

"Compsoer-ready" does not mean mix-and-match, though; once you start using Composer on an individual Drupal site, you cannot go back to drush pm-update and friends.

Our goal, then, is that if #autoupdates does not support Composer use as an update method initially that it at least maintain the "Composer ready" nature of the tarball downloads.

If the result of applying a quasi-patch zip file to a site always produces the same result as un-taring the equivalent tarball download on top of the same site (i.e. it is not missing any files except files that did not change), then the "Composer-ready" aspect of the site should be maintained.

If that's the case, I think that's sufficient for now. Of course, autoupdates will have to use Composer to update for sites that have used Composer to change their vendor directory, but I think it's okay if that is follow-on work, after the quasi-patch technique, if that's the desired development order here.

heddn’s picture

From the slack conversation during the regular scheduled autoupdates meeting today:

  1. Tarballs and drush or gzip’ed downloads of modules. 100% fully supported. no issues.
  2. Site converted to composer ready project (drush pm-update in Drupal 8.8.0). Yeah!!! No additional modifications to anything besides what comes out of the gate from the conversion to composer. This should work in all cases, just like the previous tarball or drush install scenarios.
  3. Commerce site with lots of changes and additions via composer. This is basically scenario two in many ways but is 1) more concrete and 2) has a modified composer.lock file and modified vendor folder. This should also mostly just work, unless you’ve updated to a later version of symfony or twig or something than what d.o. says you should have. If the site has updated something from vendor, then we wouldn’t want to do harm and replace it with something potentially older or contrary to what is already installed. They don’t match the hash from d.o and we’d fail to update you. Because we’d be potentially causing more harm than good by overwriting some hacked or patches version of those files.
heddn’s picture

Status: Needs work » Needs review
Related issues:

The failures in #19 are due to us finally reaching a point where we need #3031379: Add a new test type to do real update testing

heddn’s picture

OK, this builds off #3031379: Add a new test type to do real update testing. It passes locally, let's see if we got all the right things configured for testbot running.

heddn’s picture

heddn’s picture

FileSize
1.81 KB
523.22 KB

Fixing phpcs

Status: Needs review » Needs work

The last submitted patch, 26: 3055872-25.patch, failed testing. View results

heddn’s picture

FileSize
23.75 KB
1.31 KB
heddn’s picture

Status: Needs work » Needs review
FileSize
23.75 KB
heddn’s picture

FileSize
523.86 KB
heddn’s picture

This is now using the zip file format that @drumm plans to use on d.o. If this passes green (it did locally) it is ready for reviews.

I plan to do the signature validation in a follow-up.

Status: Needs review » Needs work

The last submitted patch, 31: 3055872-31.patch, failed testing. View results

heddn’s picture

Status: Needs work » Needs review
FileSize
328.56 KB
heddn’s picture

Related issues:
FileSize
393.11 KB
7.3 KB

Here's the start of adding test coverage for contrib projects. It doesn't pass locally, but its some more progress.

Status: Needs review » Needs work

The last submitted patch, 34: 3055872-34.patch, failed testing. View results

heddn’s picture

Status: Needs work » Needs review
FileSize
4.87 KB
31.31 KB

This ran green on local now for contrib testing.

heddn’s picture

Uff, --binary

heddn’s picture

Add that newline for code standards. But otherwise, this is green and ready for reviews.

heddn’s picture

FileSize
29.11 KB
4.48 KB

We already depend on d.o assets for composer install and other things, let's go the full way and do that for the update assets too.

eiriksm’s picture

Status: Needs review » Needs work

Wow this looks awesome!

There are a couple of things I had to figure out here, for testing. It currently uses a drupal.org link to find the quasi patch zips, and just trying to update a module I had locally (admin_toolbar) did not work. I assume because only token and bootstrap are available yet? Maybe we should update the IS with that?

Also, I had to look for a while to find out where I would get the QuickStartTestBase from, but figured out it was from this issue: #3031379: Add a new test type to do real update testing

So, code looks pretty solid. Some minor things and nits mostly from me :)

  1. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,432 @@
    +  const CHECKSUM_LIST = 'checksumlist.csig';
    

    This is currently not being used for anything. Is it intended to be used later?

  2. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,432 @@
    +    $url = $this->buildUrl($project_name, $this->getQuasiPatchFileName($project_name, $from_version, $to_version));
    

    This can theoretically return FALSE or throw an exception. Seems we should at least check for a FALSE value here?

  3. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,432 @@
    +        '@version' => $version,
    

    $version is not defined. I think you meant $to_version ? :)

  4. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,432 @@
    +      $file_path = substr($file->getRealPath(), strlen($this->getTempDirectory() . self::ARCHIVE_DIRECTORY));
    

    getRealPath can return a bool, which does not go well together with substr. Maybe check for that?

  5. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,432 @@
    +        $result = $this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);
    

    The variable result is never used for anything

  6. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,432 @@
    +        $this->fileSystem->copy($file->getRealPath(), $real_path, FileSystemInterface::EXISTS_REPLACE);
    

    Another one with a potential bool|string

  7. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,432 @@
    +      $file_path = substr($file->getRealPath(), strlen($this->backup));
    

    Another one with a potential bool|string

  8. +++ b/src/Services/InPlaceUpdate.php
    @@ -0,0 +1,432 @@
    +        $this->fileSystem->copy($file->getRealPath(), $this->getRealPath($file_path, $project_root), FileSystemInterface::EXISTS_REPLACE);
    

    bool|string again

heddn’s picture

Issue summary: View changes
Status: Needs work » Needs review
heddn’s picture

FileSize
10.61 KB
32.16 KB

This fixes some of the feedback from #41. I'm not sure how getRealPath could return a non-string though, so I didn't fix that.

This also adds more test coverage.

heddn’s picture

41.2 seems like it would throw an exception if the URL is malformed, not an empty string. I don't want to catch that but rather things should be loud and noisy in that case:

   * @throws \InvalidArgumentException
   *   Thrown when the passed in path has no scheme.

41.4:

  protected function getRealPath($file_path, $project_root) {
    if (substr($file_path, 0, 6) === 'vendor/') {
      return $this->vendorPath . substr($file_path, 7);
    }
    return rtrim($project_root, '/\\') . DIRECTORY_SEPARATOR . $file_path;
  }

rtrim always returns a string. If substr returns false, it won't match 'vendor'. It seems like all cases, we get a string, no?

heddn’s picture

Issue summary: View changes
eiriksm’s picture

Sorry I was not more explicit. But I was talking about $file->getRealPath():

https://www.php.net/manual/en/splfileinfo.getrealpath.php

Returns the path to the file, or FALSE if the file does not exist.

eiriksm’s picture

Although to be fair, that would be pretty unlikely, since we filter for $file->isFile() not long before. As I said. Very nit, sorry :)

Otherwise I feel my feedback was very well addressed, nice update in the IS as well.

heddn’s picture

FileSize
5.45 KB
5.45 KB
heddn’s picture

eiriksm’s picture

Status: Needs review » Reviewed & tested by the community

Thanks, looking great!

heddn’s picture

FileSize
36.18 KB
10.93 KB
heddn’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
1.94 KB
35.34 KB

The actual code under test still hasn't changed since #51. All that is changing is updates for chasing core HEAD and its new test type in #3031379: Add a new test type to do real update testing.

If this still is green after a test run, I'm going to land this thing today.

heddn’s picture

Status: Needs review » Needs work

The last submitted patch, 54: 3055872-54.patch, failed testing. View results

heddn’s picture

Status: Needs work » Needs review
FileSize
1.14 KB
35.47 KB
heddn’s picture

  • heddn committed c72c1bd on 8.x-1.x
    Issue #3055872 by heddn, catch, eiriksm: In place updates (from quasi-...
heddn’s picture

Status: Needs review » Fixed

Thanks for all the reviews.

hestenet’s picture

Woohoo!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.