woocommerce / qit-cli

A Testing Platform for WordPress Plugins and Themes
https://qit.woo.com
19 stars 2 forks source link

Local test env tweaks #147

Closed Luc45 closed 7 months ago

Luc45 commented 7 months ago

This PR does some final tweaks for the local test env:

This PR is complemented by the documentation PR on the Docs repo.

Testing Instructions

Testing shortcuts

Create a config file, eg: qit-env.yml

plugins:
  - gutenberg
  - wp-staging
themes:
  - storefront

Testing Custom Handlers

This one is a bit trickier to test.

Place this qit-env.yml file on your dir:

plugins:
  - example-public-plugin
  - example-plugin
  - example-private-build-plugin
requires:
  - public-handler.php
  - private-handler.php
  - advanced-handler.php

Create these 3 files below, run qit env:up -vvv and assert that it downloaded the custom plugins from custom sources. Public/private handlers will download from a public/private GH repos, and advanced handler will download from a private repo, build it, and cache the build. It will only rebuild if the repo changes on the remote.

I have granted @zhongruige and @MrJnrman access to the private repos needed for this test, just accept the invitation please.

public-handler.php

<?php

use QIT_CLI\Environment\ExtensionDownload\Handlers\CustomHandler;
use QIT_CLI\Environment\ExtensionDownload\Extension;

class PublicHandlerExample extends CustomHandler {
    public function should_handle( Extension $extension ): bool {
        return strpos( $extension->extension_identifier, 'example-public-plugin' ) !== false;
    }

    public function populate_extension_versions( array $extensions ): void {
        // No need to do anything here if we don't plan to cache it.
    }

    /**
     * @param array<Extension> $extensions
     */
    public function maybe_download_extensions( array $extensions, string $cache_dir ): void {
        foreach ( $extensions as $extension ) {
            if ( $this->should_handle( $extension ) ) {
                // The URL of the GitHub repository ZIP file.
                $zip_url = 'https://github.com/Luc45/example-public-plugin/archive/refs/heads/main.zip';

                // Creates a unique file name for the ZIP file.
                $zip_file = sys_get_temp_dir() . '/' . uniqid( 'my-custom-plugin-' ) . '.zip';

                if ( file_put_contents( $zip_file, file_get_contents( $zip_url ) ) === false ) {
                    throw new \Exception( "Could not download ZIP file" );
                }

                if ( file_exists( $zip_file ) ) {
                    $extension->path = $zip_file;
                } else {
                    throw new \Exception( "ZIP file not found after download" );
                }
            }
        }
    }
}

private-handler.php

<?php

use QIT_CLI\Environment\ExtensionDownload\Handlers\CustomHandler;
use QIT_CLI\Environment\ExtensionDownload\Extension;

class PrivateGitHubHandler extends CustomHandler {
    public function should_handle( Extension $extension ): bool {
        return strpos( $extension->extension_identifier, 'example-plugin' ) !== false;
    }

    public function populate_extension_versions( array $extensions ): void {
        // No need to do anything here if we don't plan to cache it.
    }

    /**
     * @param array<Extension> $extensions
     */
    public function maybe_download_extensions(array $extensions, string $cache_dir): void {
        foreach ($extensions as $extension) {
            if ($this->should_handle($extension)) {
                // Define the GitHub repository URL.
                $repo_url = 'git@github.com:Luc45/example-plugin.git';
                $branch = 'main';

                // Create a unique directory and file name for the repository clone and ZIP file.
                $repo_dir = sys_get_temp_dir() . '/' . uniqid('my-custom-plugin-');
                $zip_file = $repo_dir . '.zip';

                // Git commands to clone the repository and create a ZIP archive of the specified branch.
                $git_clone_cmd = "git clone --branch $branch $repo_url $repo_dir";
                $git_archive_cmd = "git -C $repo_dir archive --format=zip --output $zip_file HEAD";

                // Execute the Git clone command.
                exec( $git_clone_cmd, $output, $result_code );

                // Check if the clone was successful.
                if ( $result_code !== 0 ) {
                    throw new \Exception("Could not clone the repository: " . implode("\n", $output));
                }

                // Execute the Git archive command.
                exec( $git_archive_cmd, $output, $result_code );

                // Check if the archive command was successful.
                if ( $result_code !== 0 ) {
                    throw new \Exception("Could not create ZIP file: " . implode("\n", $output));
                }

                if ( file_exists( $zip_file ) ) {
                    $extension->path = $zip_file;
                } else {
                    throw new \Exception("ZIP file not found after creation");
                }
            }
        }
    }
}

advanced-handler.php

<?php

use QIT_CLI\Environment\ExtensionDownload\Handlers\CustomHandler;
use QIT_CLI\Environment\ExtensionDownload\Extension;

class AdvancedGitHubHandler extends CustomHandler {
    public function should_handle( Extension $extension ): bool {
        // If the plugin slug is "my-custom-plugin", we handle it here.
        return strpos( $extension->extension_identifier, 'example-private-build-plugin' ) !== false;
    }

    /**
     * The only purpose of this method is to set "$extension->version" on the extensions
     * that it handles. It's fine to ignore this method completely if you don't plan to
     * support caching (eg: you will download it every time the environment runs).
     *
     * @param array<Extension> $extensions An array with all extensions to install in the environment.
     *
     * @return void
     */
    public function populate_extension_versions( array $extensions ): void {
        foreach ( $extensions as $extension ) {
            if ( $this->should_handle( $extension ) ) {
                /*
                 * In this example, we are installing a plugin that exists in a remote GitHub Repository,
                 * so we will set $extension->version to the last commit ID on remote.
                 */

                // Here we set an example GitHub repository that we will be reading from. This could be a private repo, for instance.
                $repo_url = 'git@github.com:Luc45/example-private-build-plugin.git';

                /*
                 * We will hardcode the branch to keep the example simple, but we could
                 * get creative and use a format like this "my-custom-plugin#my-branch":
                 *
                 * [ $repo, $branch ] = explode( '#', $extension->extension_identifier );
                 */
                $branch = 'main';

                // Example output: "cde7540d569b29772f95161513835e1a596c419c        refs/heads/trunk"
                exec( "git ls-remote $repo_url $branch", $output, $result_code );

                if ( $result_code !== 0 || ! isset( $output[0] ) ) {
                    throw new \Exception( "Could not fetch the latest commit ID" );
                }

                /*
                 * Here we explode the first line of the output ($output[0]),
                 * using the tab character as the delimiter, and we get the first element,
                 * which will be the commit ID.
                 */
                $latest_commit_id = explode( "\t", $output[0] )[0];

                // Set the version to the latest commit ID.
                // This way, we will always use a local cache, and only download it again when the remote ID changes.
                $extension->version = $latest_commit_id;
            }
        }
    }

    public function maybe_download_extensions( array $extensions, string $cache_dir ): void {
        foreach ( $extensions as $extension ) {
            if ( $this->should_handle( $extension ) ) {
                $cache_path = $this->make_cache_path( $cache_dir, $extension->type, $extension->extension_identifier, $extension->version, '-' );

                // Cache hit?
                if ( file_exists( $cache_path ) ) {
                    if ( $this->output->isVeryVerbose() ) {
                        $this->output->writeln( "Using cached {$extension->type} {$extension->extension_identifier}." );
                    }
                    $extension->path = $cache_path;
                    continue;
                } else {
                    if ( $this->output->isVeryVerbose() ) {
                        $this->output->writeln( "Cache miss on {$extension->type} {$extension->extension_identifier}." );
                    }

                    // Creates a temporary directory to clone the GitHub repository.
                    $repo_dir = sys_get_temp_dir() . '/' . uniqid( 'my-custom-plugin-' );

                    // Clones it using `git`, which has authentication already.
                    exec( "git clone git@github.com:Luc45/example-private-build-plugin.git $repo_dir 2>&1", $output, $result_code );

                    if ( $result_code !== 0 ) {
                        throw new \Exception( "Could not clone repository" );
                    }

                    if ( $this->output->isVerbose() ) {
                        foreach ( $output as $line ) {
                            $this->output->writeln( $line );
                        }
                    }

                    // Changes the current directory to the cloned repository.
                    chdir( $repo_dir );

                    // Assuming your extension has a build script, we can run it here.
                    exec( "npm run build", $output, $result_code );

                    if ( $result_code !== 0 ) {
                        throw new \Exception( "Build failed" );
                    }

                    if ( $this->output->isVerbose() ) {
                        foreach ( $output as $line ) {
                            $this->output->writeln( $line );
                        }
                    }

                    // Assuming 'npm run build' creates a zip file named 'my-build.zip'
                    $build_zip = "$repo_dir/my-build.zip";

                    // Save to cache.
                    if ( ! rename( $build_zip, $cache_path ) ) {
                        throw new \Exception( "Could not move the build zip to the cache path" );
                    }

                    if ( file_exists( $cache_path ) ) {
                        // This is ultimately what this method should do: set the path to the zip file on $extension.
                        $extension->path = $cache_path;
                    } else {
                        throw new \Exception( "Build failed" );
                    }
                }
            }
        }
    }
}
zhongruige commented 7 months ago

I also got this when trying to bring up the env:

Screenshot 2024-03-15 at 10 22 42 AM

My qit-env.yml file is the example included in the description:

plugins:
  - gutenberg
  - wp-staging
themes:
  - storefront

Made sure I ran a make build so I'd be on the latest here, too.

Luc45 commented 7 months ago

I also got this when trying to bring up the env:

What backend are you connected to? Point it to prod, please

Luc45 commented 7 months ago

If you want to use local, you'd have to:

This would force it to fetch the latest environment from upstream jfyi. But switching to prod is the easiest.

zhongruige commented 7 months ago

What backend are you connected to? Point it to prod, please

Hm yeah I was connected to prod:

Screenshot 2024-03-15 at 10 59 53 AM