WordPress.org

Making WordPress.org

Changeset 5147


Ignore:
Timestamp:
03/13/2017 05:56:27 AM (3 years ago)
Author:
dd32
Message:

Plugin Directory: ZIPs: Do not build zips on demand, instead store them within a SVN repository.

See #1578

Location:
sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory
Files:
1 deleted
7 edited
1 copied

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/class-base.php

    r4301 r5147  
    2020        new Routes\Query_Plugins();
    2121        new Routes\SVN_Access();
    22         new Routes\Zip_Management();
    2322        new Routes\Plugin_Committers();
    2423    }
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/bin/rebuild-zip.php

    r5139 r5147  
    11<?php
    22namespace WordPressdotorg\Plugin_Directory;
     3use WordPressdotorg\Plugin_Directory\Tools\SVN;
    34
    45// This script should only be called in a CLI environment.
     
    78}
    89
    9 ob_start();
     10//ob_start();
    1011
    11 $opts = getopt( '', array( 'url:', 'abspath:', 'plugin:', 'changed-tags:', 'async' ) );
     12$opts = getopt( '', array( 'url:', 'abspath:', 'plugin:', 'versions:', 'async' ) );
    1213
    1314// Guess the default parameters:
     
    1516    $opts['plugin'] = $argv[1];
    1617    $argv[1] = '--plugin ' . $argv[1];
     18}
     19if ( empty( $opts ) && $argc == 3 ) {
     20    $opts['plugin'] = $argv[1];
     21    $argv[1] = '--plugin ' . $argv[1];
     22
     23    $opts['versions'] = $argv[2];
     24    $argv[2] = '--versions ' . $argv[2];
    1725}
    1826if ( empty( $opts['url'] ) ) {
     
    2230    $opts['abspath'] = substr( __DIR__, 0, strpos( __DIR__, 'wp-content' ) );
    2331}
    24 
    25 if ( empty( $opts['changed-tags'] ) ) {
    26     $opts['changed-tags'] = array( 'trunk' );
    27 } else {
    28     $opts['changed-tags'] = explode( ',', $opts['changed-tags'] );
     32if ( empty( $opts['versions'] ) ) {
     33    $opts['versions'] = '';
    2934}
    30 
    31 $opts['async'] = isset( $opts['async'] );
    3235
    3336foreach ( array( 'url', 'abspath', 'plugin' ) as $opt ) {
     
    3639        fwrite( STDERR, "Usage: php {$argv[0]} --plugin hello-dolly --abspath /home/example/public_html --url https://wordpress.org/plugins/\n" );
    3740        fwrite( STDERR, "--url and --abspath will be guessed if possible.\n" );
    38         die();
     41        exit(1);
    3942    }
    4043}
     
    5255        fwrite( STDERR, "\tphp " . implode( ' ', $argv ) . " --url " . get_site_url( WPORG_PLUGIN_DIRECTORY_BLOGID, '/' ) . "\n" );
    5356    }
    54     die();
     57    exit(1);
    5558}
    5659
    57 $plugin_slug  = $opts['plugin'];
    58 $changed_tags = $opts['changed-tags'];
    59 $start_time   = microtime(1);
     60$plugin_slug = $opts['plugin'];
     61$versions    = array_filter( array_unique( array_map( 'trim', (array) explode( ',', $opts['versions'] ) ) ), 'strlen' );
     62$start_time  = microtime(1);
    6063
    61 // If async, queue it to be parsed instead.
    62 if ( $opts['async'] ) {
    63     Jobs\Plugin_Import::queue( $plugin_slug, array( 'tags_touched' => $changed_tags ) );
    64     echo "Queueing Import for $plugin_slug... OK\n";
    65     die();
     64if ( empty( $versions ) ) {
     65    // Rebuild them all!
     66    $svn_tags = SVN::ls( "http://plugins.svn.wordpress.org/{$plugin_slug}/tags/" );
     67    if ( false === $svn_tags ) {
     68        fwrite( STDERR, "Error! Failed to retrieve SVN tag listing." );
     69        exit(1);
     70    }
     71
     72    $versions = array_map(
     73        function( $dir ) { return trim( $dir, '/' ); },
     74        $svn_tags
     75    );
     76    $versions[] = 'trunk';
    6677}
    6778
    68 echo "Processing Import for $plugin_slug... ";
     79if ( ! $versions ) {
     80    fwrite( STDERR, "Error! No versions specified (or we couldn't find any)" );
     81    exit(1);
     82}
     83
     84echo "Rebuilding ZIPs for $plugin_slug... ";
    6985try {
    70     $importer = new CLI\Import;
    71     $importer->import_from_svn( $plugin_slug, $changed_tags );
     86    $zip_builder = new ZIP\Builder();
     87    $zip_builder->build(
     88        $plugin_slug,
     89        $versions,
     90        "{$plugin_slug}: Rebuild triggered by " . php_uname('n' )
     91    );
     92
    7293    echo "OK. Took " . round( microtime(1) - $start_time, 2 )  . "s\n";
    7394} catch( \Exception $e ) {
    74     echo "Failed. Took " . round( microtime(1) - $start_time, 2 )  . "s\n";
    75 
    76     fwrite( STDERR, "[{$plugin_slug}] Plugin Import Failed: " . $e->getMessage() . "\n" );
     95    fwrite( STDERR, "{$plugin_slug}: Zip Rebuild failed: " . $e->getMessage() . "\n" );
    7796    exit(1);
    7897}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-directory.php

    r4764 r5147  
    121121                'publish_posts'      => 'plugin_approve',
    122122                'read_private_posts' => 'do_not_allow',
    123                 'delete_posts'       => 'do_not_allow',
     123                'delete_posts'       => is_super_admin() ? 'manage_options' : 'do_not_allow',
    124124                'create_posts'       => 'do_not_allow',
    125125            ),
     
    10061006     */
    10071007    function custom_redirects() {
    1008 
    10091008        // Handle a redirect for /$plugin/$tab_name/ to /$plugin/#$tab_name.
    10101009        if ( get_query_var( 'redirect_plugin_tab' ) ) {
     
    10311030            // The about page is now over at /developers/.
    10321031            if ( 'about' === $path[2] ) {
    1033                 wp_safe_redirect( home_url( '/developers/' . ( ( isset( $path[3] ) && 'add' == $path[3] ) ? 'add/' : '' ) ) );
     1032                if ( isset( $path[3] ) && 'add' == $path[3] ) {
     1033                    wp_safe_redirect( home_url( '/developers/add/' ) );
     1034                } elseif ( isset( $path[3] ) && 'validator' == $path[3] ) {
     1035                    wp_safe_redirect( home_url( '/developers/readme-validator/' ) );
     1036                } else {
     1037                    wp_safe_redirect( home_url( '/developers/' ) );
     1038                }
    10341039                die();
    10351040            }
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php

    r4727 r5147  
    88use WordPressdotorg\Plugin_Directory\Tools\Filesystem;
    99use WordPressdotorg\Plugin_Directory\Tools\SVN;
     10use WordPressdotorg\Plugin_Directory\Zip\Builder;
    1011use Exception;
    1112
     
    4748     * @throws \Exception
    4849     *
    49      * @param string $plugin_slug      The slug of the plugin to import.
    50      * @param array  $svn_changed_tags A list of tags/trunk which the SVN change touched. Optional.
    51      */
    52     public function import_from_svn( $plugin_slug, $svn_changed_tags = array( 'trunk' ) ) {
     50     * @param string $plugin_slug            The slug of the plugin to import.
     51     * @param array  $svn_changed_tags       A list of tags/trunk which the SVN change touched. Optional.
     52     * @param array  $svn_revision_triggered The SVN revision which this import has been triggered by.
     53     */
     54    public function import_from_svn( $plugin_slug, $svn_changed_tags = array( 'trunk' ), $svn_revision_triggered = 0 ) {
    5355        global $wpdb;
    5456
     
    190192        $current_stable_tag = get_post_meta( $plugin->ID, 'stable_tag', true ) ?: 'trunk';
    191193
    192         $this->rebuild_invalidate_zips( $plugin_slug, $stable_tag, $current_stable_tag, $svn_changed_tags );
     194        $this->rebuild_affected_zips( $plugin_slug, $stable_tag, $current_stable_tag, $svn_changed_tags, $svn_revision_triggered );
    193195
    194196        // Finally, set the new version live.
     
    202204
    203205    /**
    204      * Rebuild and Invalidate plugin ZIPs on all web nodes using the REST API Endpoints.
    205      *
    206      * @param string $plugin_slug        The plugin slug.
    207      * @param string $stable_tag         The new stable tag.
    208      * @param string $current_stable_tag The new stable tag.
    209      * @param array  $svn_changed_tags   The list of SVN tags modified since last import.
    210      */
    211     protected function rebuild_invalidate_zips( $plugin_slug, $stable_tag, $current_stable_tag, $svn_changed_tags ) {
    212         global $wporg_webs;
    213         $invalidate_zips = $rebuild_zips = array();
    214 
    215         foreach ( $svn_changed_tags as $tag ) {
    216             if ( 'trunk' == $tag ) {
    217                 if ( 'trunk' == $stable_tag ) {
    218                     // Trunk is stable, so we'll need to rebuild the zip
    219                     $rebuild_zips[] = "{$plugin_slug}.zip";
    220                 } else {
    221                     // Trunk isn't stable, so we'll just remove it so it's rebuilt on demand
    222                     $invalidate_zips[] = "{$plugin_slug}.zip";
    223                 }
    224                 continue;
    225             }
    226             if ( $tag == $stable_tag || $tag == $current_stable_tag ) {
    227                 $rebuild_zips[] = "{$plugin_slug}.{$tag}.zip";
    228             } else {
    229                 $invalidate_zips[] = "{$plugin_slug}.{$tag}.zip";
    230             }
    231         }
    232         if ( $stable_tag != $current_stable_tag ) {
    233             // plugin is updated, ensure that everything is rebuilt.
    234             if ( ! in_array( $stable_tag, $svn_changed_tags ) ) {
    235                 $rebuild_zips[] = "{$plugin_slug}" . ( 'trunk' == $stable_tag ? '' : ".{$stable_tag}" ) . '.zip';
    236             }
    237         }
    238 
    239         if ( empty( $wporg_webs ) || ( empty( $invalidate_zips ) && empty( $rebuild_zips ) ) ) {
    240             return;
    241         }
    242 
    243         $urls = array();
    244         foreach ( $wporg_webs as $node ) {
    245             $urls[] = preg_replace( '!^https?://wordpress.org/!', "http://$node/", site_url( '/wp-json/plugins/v1/zip-management' ) );
    246         }
    247         $headers = array(
    248             'User-Agent' => 'WordPress.org Plugin Directory',
    249             'Host' => 'WordPress.org',
    250             'Authorization' => 'BEARER ' . PLUGIN_API_INTERNAL_BEARER_TOKEN,
    251         );
    252         $body = array(
    253             'plugins' => array(
    254                 $plugin_slug => array(
    255                     'invalidate' => $invalidate_zips,
    256                     'rebuild' => $rebuild_zips,
    257                 )
    258             )
    259         );
    260 
    261         $results = array();
    262         foreach ( $urls as $url ) {
    263             $results[ $url ] = wp_remote_post( $url, array(
    264                 'body' => $body,
    265                 'headers' => $headers,
    266                 'sslverify' => false
    267             ) );
    268         }
    269 
    270         // TODO Do something with $results to verify all servers said the rebuilt zip was correct or something.
     206     * (Re)build plugin ZIPs affected by this commit.
     207     *
     208     * @param string $plugin_slug            The plugin slug.
     209     * @param string $stable_tag             The new stable tag.
     210     * @param string $current_stable_tag     The new stable tag.
     211     * @param array  $svn_changed_tags       The list of SVN tags modified since last import.
     212     * @param string $svn_revision_triggered The SVN revision which triggered the rebuild.
     213     *
     214     * @return bool
     215     */
     216    protected function rebuild_affected_zips( $plugin_slug, $stable_tag, $current_stable_tag, $svn_changed_tags, $svn_revision_triggered = 0 ) {
     217        $versions_to_build = $svn_changed_tags;
     218
     219        // Ensure that the stable zip is built/rebuilt if need be.
     220        if ( $stable_tag != $current_stable_tag && ! in_array( $stable_tag, $versions_to_build ) ) {
     221            $versions_to_build[] = $stable_tag;
     222        }
     223
     224        // Rebuild/Build $build_zips
     225        try {
     226            // This will rebuild the ZIP.
     227            $zip_builder = new Builder();
     228            $zip_builder->build(
     229                $plugin_slug,
     230                array_unique( $versions_to_build ),
     231                $svn_revision_triggered ?
     232                    "{$plugin_slug}: ZIP build triggered by https://plugins.trac.wordpress.org/changeset/{$svn_revision_triggered}" :
     233                    "{$plugin_slug}: ZIP build triggered by " . php_uname('n')
     234            );
     235        } catch( Exception $e ) {
     236            return false;
     237        }
     238
     239        return true;
    271240    }
    272241
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-plugin-import.php

    r4563 r5147  
    3434        $changed_tags = isset( $plugin_data['tags_touched'] ) ? $plugin_data['tags_touched'] : array( 'trunk' );
    3535
     36        $revision = isset( $plugin_data['revisions'] ) ? max( (array)$plugin_data['revisions'] ) : false;
     37
    3638        try {
    3739            $importer = new CLI\Import;
    38             $importer->import_from_svn( $plugin_slug, $changed_tags );
     40            $importer->import_from_svn( $plugin_slug, $changed_tags, $revision );
    3941        } catch( Exception $e ) {
    4042            fwrite( STDERR, "[{$plugin_slug}] Plugin Import Failed: " . $e->getMessage() . "\n" );
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/tools/class-svn.php

    r3512 r5147  
    2222     * }
    2323     */
    24     public static function import( $path, $url, $message ) {
    25         $options     = array(
    26             'non-interactive',
    27             'm'    => $message,
    28             'user' => PLUGIN_SVN_MANAGEMENT_USER,
    29             'pass' => PLUGIN_SVN_MANAGEMENT_PASS,
    30         );
     24    public static function import( $path, $url, $message, $options = array() ) {
     25        $options[] = 'non-interactive';
     26        $options['m'] = $message;
     27        if ( empty( $options['username'] ) ) {
     28            $options['username'] = PLUGIN_SVN_MANAGEMENT_USER;
     29            $options['password'] = PLUGIN_SVN_MANAGEMENT_PASS;
     30        }
     31
    3132        $esc_options = self::parse_esc_parameters( $options );
    3233
     
    6869        $output = self::shell_exec( "svn export $esc_options $esc_url $esc_destination 2>&1" );
    6970        if ( preg_match( '/Exported revision (?P<revision>\d+)[.]/i', $output, $m ) ) {
     71            $revision = (int) $m['revision'];
     72            $result   = true;
     73        } else {
     74            $result = false;
     75            $errors = self::parse_svn_errors( $output );
     76        }
     77
     78        return compact( 'result', 'revision', 'errors' );
     79    }
     80
     81    /**
     82     * Create an SVN Checkout of a URL to a local directory.
     83     *
     84     * @static
     85     *
     86     * @param string $url         The URL to export.
     87     * @param string $destination The local folder to checkout into.
     88     * @param array  $options     Optional. A list of options to pass to SVN. Default: empty array.
     89     * @return array {
     90     *     @type bool $result   The result of the operation.
     91     *     @type int  $revision The revision exported.
     92     * }
     93     */
     94    public static function checkout( $url, $destination, $options = array() ) {
     95        $options[]   = 'non-interactive';
     96        $esc_options = self::parse_esc_parameters( $options );
     97
     98        $esc_url         = escapeshellarg( $url );
     99        $esc_destination = escapeshellarg( $destination );
     100
     101        $output = self::shell_exec( "svn checkout $esc_options $esc_url $esc_destination 2>&1" );
     102        if ( preg_match( '/Checked out revision (?P<revision>\d+)[.]/i', $output, $m ) ) {
     103            $revision = (int) $m['revision'];
     104            $result   = true;
     105        } else {
     106            $result = false;
     107            $errors = self::parse_svn_errors( $output );
     108        }
     109
     110        return compact( 'result', 'revision', 'errors' );
     111    }
     112
     113    /**
     114     * Update a SVN checkout.
     115     *
     116     * @static
     117     *
     118     * @param string $checkout The path of the SVN checkout to update.
     119     * @param array  $options  Optional. A list of options to pass to SVN. Default: empty array.
     120     * @return array {
     121     *     @type bool $result   The result of the operation.
     122     *     @type int  $revision The revision exported.
     123     * }
     124     */
     125    public static function up( $checkout, $options = array() ) {
     126        $options[]   = 'non-interactive';
     127        $esc_options = self::parse_esc_parameters( $options );
     128
     129        $esc_checkout = escapeshellarg( $checkout );
     130
     131        $output = self::shell_exec( "svn up $esc_options $esc_checkout 2>&1" );
     132        if ( preg_match( '/Updated to revision (?P<revision>\d+)[.]/i', $output, $m ) ) {
     133            $revision = (int) $m['revision'];
     134            $result   = true;
     135        } else {
     136            $result = false;
     137            $errors = self::parse_svn_errors( $output );
     138        }
     139
     140        return compact( 'result', 'revision', 'errors' );
     141    }
     142
     143    /**
     144     * Add a file in a SVN checkout to be revisioned.
     145     *
     146     * @static
     147     *
     148     * @param string $checkout The path of the file to add to SVN.
     149     * @return array {
     150     *     @type bool $result   The result of the operation.
     151     * }
     152     */
     153    public static function add( $file ) {
     154        $options[]   = 'non-interactive';
     155        $esc_options = self::parse_esc_parameters( $options );
     156
     157        $esc_file     = escapeshellarg( $file );
     158
     159        $output = self::shell_exec( "svn add $esc_options $esc_file 2>&1" );
     160        if ( preg_match( "/^A/i", $output ) ) {;
     161            $result   = true;
     162        } else {
     163            $result = false;
     164            $errors = self::parse_svn_errors( $output );
     165        }
     166
     167        return compact( 'result', 'errors' );
     168    }
     169
     170    /**
     171     * Commit changes in a SVN checkout.
     172     *
     173     * @static
     174     *
     175     * @param string $checkout The local folder to import into SVN.
     176     * @param string $message  The commit message.
     177     * @param array  $options  Any specific options to pass to SVN.
     178     * @return array {
     179     *     @type bool $result   The result of the operation.
     180     *     @type int  $revision The revision imported.
     181     * }
     182     */
     183    public static function commit( $checkout, $message, $options = array() ) {
     184        $options[] = 'non-interactive';
     185        $options['m'] = $message;
     186        if ( empty( $options['username'] ) ) {
     187            $options['username'] = PLUGIN_SVN_MANAGEMENT_USER;
     188            $options['password'] = PLUGIN_SVN_MANAGEMENT_PASS;
     189        }
     190
     191        $esc_options = self::parse_esc_parameters( $options );
     192
     193        $esc_checkout = escapeshellarg( $checkout );
     194
     195        $output = self::shell_exec( "svn commit $esc_options $esc_checkout 2>&1" );
     196        if ( preg_match( '/Committed revision (?P<revision>\d+)[.]/i', $output, $m ) ) {
    70197            $revision = (int) $m['revision'];
    71198            $result   = true;
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/zip/class-builder.php

    r4727 r5147  
    1111class Builder {
    1212
    13     /**
    14      * The base directory for the ZIP files.
    15      * Zip files will be stored in a sub-directory, such as:
    16      * /tmp/plugin-zipfiles/hello-dolly/hello-dolly.zip
    17      */
    18     const ZIP_DIR = '/tmp/plugin-zipfiles';
    19     const SVN_URL = 'https://plugins.svn.wordpress.org';
    20 
    21     public $zip_file = '';
    22     public $md5_file = '';
    23     protected $tmp_build_file = '';
     13    const TMP_DIR = '/tmp/plugin-zip-builder';
     14    const SVN_URL = 'http://plugins.svn.wordpress.org';
     15    const ZIP_SVN_URL = PLUGIN_ZIP_SVN_URL;
     16
     17    protected $zip_file = '';
    2418    protected $tmp_build_dir  = '';
    25 
    26 
    27     /**
    28      * Generate a ZIP for a provided Plugin Version.
    29      *
    30      * @param string $plugin_slug The Plugin slug
    31      * @param string $version     The version to build (tag, or trunk)
    32      */
    33     public function __construct( $slug, $version ) {
    34         if ( ! is_dir( self::ZIP_DIR ) ) {
    35             mkdir( self::ZIP_DIR, 0777, true );
    36             chmod( self::ZIP_DIR, 0777 );
    37         }
    38         if ( ! is_dir( self::ZIP_DIR . '/' . $slug ) ) {
    39             mkdir( self::ZIP_DIR . '/' . $slug, 0777, true );
    40             chmod( self::ZIP_DIR . '/' . $slug, 0777 );
    41         }
    42 
    43         $this->slug = $slug;
    44         $this->version = $version;
    45 
    46         if ( 'trunk' == $this->version ) {
    47             $this->zip_file = self::ZIP_DIR . "/{$this->slug}/{$this->slug}.zip";
     19    protected $tmp_dir = '';
     20
     21    protected $slug    = '';
     22    protected $version = '';
     23    protected $context = '';
     24
     25    /**
     26     * Generate a ZIP for a provided Plugin versions.
     27     *
     28     * @param string $slug     The plugin slug.
     29     * @param array  $versions The versions of the plugin to build ZIPs for.
     30     * @param string $context  The context of this Builder instance (commit #, etc)
     31     */
     32    public function build( $slug, $versions, $context = '' ) {
     33        // Bail when in an unconfigured environment.
     34        if ( ! defined( 'PLUGIN_ZIP_SVN_URL' ) ) {
     35            return false;
     36        }
     37
     38        $this->slug     = $slug;
     39        $this->versions = $versions;
     40        $this->context  = $context;
     41
     42        // General TMP directory
     43        if ( ! is_dir( self::TMP_DIR ) ) {
     44            mkdir( self::TMP_DIR, 0777, true );
     45            chmod( self::TMP_DIR, 0777 );
     46        }
     47
     48        // Temp Directory for this instance of the Builder class.
     49        $this->tmp_dir = $this->generate_temporary_directory( self::TMP_DIR, $slug );
     50
     51        // Create a checkout of the ZIP SVN
     52        $res_checkout = SVN::checkout(
     53            self::ZIP_SVN_URL,
     54            $this->tmp_dir,
     55            array(
     56                'depth' => 'empty',
     57                'username' => PLUGIN_ZIP_SVN_USER,
     58                'password' => PLUGIN_ZIP_SVN_PASS,
     59            )
     60        );
     61
     62        if ( $res_checkout['result'] ) {
     63
     64            // Ensure the plugins folder exists within svn
     65            $plugin_folder = "{$this->tmp_dir}/{$this->slug}/";
     66            $res = SVN::up(
     67                $plugin_folder,
     68                array(
     69                    'depth' => 'empty'
     70                )
     71            );
     72            if ( ! is_dir( $plugin_folder ) ) {
     73                mkdir( $plugin_folder, 0777, true );
     74                $res = SVN::add( $plugin_folder );
     75            }
     76            if ( ! $res['result'] ) {
     77                throw new Exception( __METHOD__ . ": Failed to create {$plugin_folder}." );
     78            }
    4879        } else {
    49             $this->zip_file = self::ZIP_DIR . "/{$this->slug}/{$this->slug}.{$this->version}.zip";
    50         }
    51         $this->md5_file = $this->zip_file . '.md5';
    52 
    53     }
    54 
    55     /**
    56      * Generate a ZIP for the plugin + version
    57      */
    58     public function build() {
    59         try {
    60             $this->tmp_build_file = $this->generate_temporary_filename( dirname( $this->zip_file ), "tmp-{$this->slug}.{$this->version}", '.zip' );
    61             $this->tmp_build_dir  = $this->tmp_build_file . '-files';
    62             mkdir( $this->tmp_build_dir, 0777, true );
    63 
    64             $this->export_plugin();
    65             $this->fix_directory_dates();           
    66             $this->generate_zip();
    67             $this->move_into_place();
    68             $this->generate_md5();
    69 
    70             $this->cleanup();
    71 
    72             return true;
    73         } catch( Exception $e ) {
    74             $this->cleanup();
    75             throw $e;
    76         }/* finally { // PHP 5.5+, meta.svn is limited to PHP 5.4 code still.
    77             $this->cleanup();
    78         }*/
    79     }
    80 
    81     /**
    82      * Generates a temporary unique file in a given directory
     80            throw new Exception( __METHOD__ . ": Failed to create checkout of {$svn_url}." );
     81        }
     82
     83        // Build the requested ZIPs
     84        foreach ( $versions as $version ) {
     85            $this->version = $version;
     86
     87            if ( 'trunk' == $version ) {
     88                $this->zip_file = "{$this->tmp_dir}/{$this->slug}/{$this->slug}.zip";
     89            } else {
     90                $this->zip_file = "{$this->tmp_dir}/{$this->slug}/{$this->slug}.{$version}.zip";
     91            }
     92
     93            // Pull the ZIP file down we're going to modify, which may not already exist.
     94            SVN::up( $this->zip_file );
     95
     96            try {
     97
     98                $this->tmp_build_dir  = $this->zip_file . '-files';
     99                mkdir( $this->tmp_build_dir, 0777, true );
     100
     101                $this->export_plugin();
     102                $this->fix_directory_dates();           
     103                $this->generate_zip();
     104                $this->cleanup_plugin_tmp();
     105
     106            } catch( Exception $e ) {
     107                // In event of error, skip this file this time.
     108                $this->cleanup_plugin_tmp();
     109
     110                // Perform an SVN up to revert any changes made.
     111                SVN::up( $this->zip_file );
     112                continue;
     113            }
     114
     115            // Add the ZIP file to SVN - This is only really needed for new files which don't exist in SVN.
     116            SVN::add( $this->zip_file );           
     117        }
     118
     119        $res = SVN::commit(
     120            $this->tmp_dir,
     121            $this->context ? $this->context : "Updated ZIPs for {$this->slug}.",
     122            array(
     123                'username' => PLUGIN_ZIP_SVN_USER,
     124                'password' => PLUGIN_ZIP_SVN_PASS,
     125            )
     126        );
     127
     128        $this->invalidate_zip_caches( $versions );
     129
     130        $this->cleanup();
     131
     132        if ( ! $res['result'] ) {
     133            if ( $res['errors'] ) {
     134                throw new Exception( __METHOD__ . ': Failed to commit the new ZIPs: ' . $res['errors'][0]['error_message'] );
     135            } else {
     136                throw new Exception( __METHOD__ . ': Commit failed without error, maybe there were no modified files?' );
     137            }
     138        }
     139
     140        return true;
     141    }
     142
     143    /**
     144     * Generates a temporary unique directory in a given directory
    83145     *
    84146     * Performs a similar job to `tempnam()` with an added suffix and doesn't
     
    92154     * @param string $suffix The file suffix, optional.
    93155     *
    94      * @return string Filename of unique temporary file.
    95      */
    96     protected function generate_temporary_filename( $dir, $prefix, $suffix = '' ) {
     156     * @return string Path of unique temporary directory.
     157     */
     158    protected function generate_temporary_directory( $dir, $prefix, $suffix = '' ) {
    97159        $i = 0;
    98160        do {
     
    106168
    107169        fclose( $fp );
     170
     171        // Convert file to directory.
     172        unlink( $filename );
     173        if ( ! mkdir( $filename, 0777, true ) ) {
     174            throw new Exception( __METHOD__ . ': Could not convert temporary filename to directory.' );
     175        }
     176        chmod( $filename, 0777 );
    108177
    109178        return $filename;
     
    168237        ) );
    169238        if ( ! $latest_file_modified_timestamp ) {
    170             throw new Exception( _METHOD__ . ': Unable to locate the latest modified files timestamp.', 503 );
     239            throw new Exception( __METHOD__ . ': Unable to locate the latest modified files timestamp.', 503 );
    171240        }
    172241
     
    182251     */
    183252    protected function generate_zip() {
    184         // We have to remove the temporary 0-byte file first as zip will complain about not being able to find the zip structures.
    185         unlink( $this->tmp_build_file );
     253        // If we're building an existing zip, remove the existing file first.
     254        if ( file_exists( $this->zip_file ) ) {
     255            unlink( $this->zip_file );
     256        }
    186257        $this->exec( sprintf(
    187258            'cd %s && find %s -print0 | sort -z | xargs -0 zip -Xu %s 2>&1',
    188259            escapeshellarg( $this->tmp_build_dir ),
    189260            escapeshellarg( $this->slug ),
    190             escapeshellarg( $this->tmp_build_file )
     261            escapeshellarg( $this->zip_file )
    191262        ), $zip_build_output, $return_value );
    192263
     
    196267    }
    197268
    198     /**
    199      * Moves the completed ZIP into it's real-life location.
    200      */
    201     protected function move_into_place() {
    202         $this->exec( sprintf(
    203             'mv -f %s %s',
    204             escapeshellarg( $this->tmp_build_file ),
    205             escapeshellarg( $this->zip_file )
    206         ), $output, $return_value );
    207 
    208         if ( $return_value ) {
    209             throw new Exception( __METHOD__ . ': Could not move ZIP into place.', 503 );
    210         }
    211     }
    212 
    213     /**
    214      * Generates the MD5 for the ZIP file used for serving.
    215      *
    216      * This can also be used for generating a package signature in the future.
    217      */
    218     protected function generate_md5() {
    219         $this->exec( sprintf(
    220             "md5sum %s | head -c 32 > %s",
    221             escapeshellarg( $this->zip_file ),
    222             escapeshellarg( $this->md5_file )
    223         ), $output, $return_code );
    224 
    225         if ( $return_code ) {
    226             throw new Exception( __METHOD__ . ': Failed to create file checksum.', 503 );
     269
     270    /**
     271     * Purge ZIP caches after ZIP building.
     272     *
     273     * @param array $versions The list of plugin versions of modified zips.
     274     * @return bool
     275     */
     276    public function invalidate_zip_caches( $versions ) {
     277        // TODO: Implement PURGE
     278        return true;
     279        if ( ! defined( 'PLUGIN_ZIP_X_ACCEL_REDIRECT_LOCATION' ) ) {
     280            return true;
     281        }
     282
     283        foreach ( $versions as $version ) {
     284            if ( 'trunk' == $version ) {
     285                $zip = "{$this->slug}/{$this->slug}.zip";
     286            } else {
     287                $zip = "{$this->slug}/{$this->slug}.{$version}.zip";
     288            }
     289
     290            foreach ( $plugins_downloads_load_balancer /* TODO */ as $lb ) {
     291                $url = 'http://' . $lb . PLUGIN_ZIP_X_ACCEL_REDIRECT_LOCATION . $zip;
     292                wp_remote_request(
     293                    $url,
     294                    array(
     295                        'method' => 'PURGE',
     296                    )
     297                );
     298            }
    227299        }
    228300    }
     
    232304     */
    233305    protected function cleanup() {
    234         if ( $this->tmp_build_file && file_exists( $this->tmp_build_file ) ) {
    235             unlink( $this->tmp_build_file );
    236         }
     306        if ( $this->tmp_dir ) {
     307            $this->exec( sprintf( 'rm -rf %s', escapeshellarg( $this->tmp_dir ) ) );
     308        }
     309    }
     310
     311    /**
     312     * Cleans up any temporary directories created by the ZIP builder for a specific build.
     313     */
     314    protected function cleanup_plugin_tmp() {
    237315        if ( $this->tmp_build_dir ) {
    238316            $this->exec( sprintf( 'rm -rf %s', escapeshellarg( $this->tmp_build_dir ) ) );
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/zip/class-serve.php

    r4728 r5147  
    1313class Serve {
    1414
    15     const ZIP_DIR = '/tmp/plugin-zipfiles';
    16 
    1715    public function __construct() {
    1816        try {
    1917            $request = $this->determine_request();
    2018
    21             // Serve & perhaps build if need be
    22             $files = $this->get_files( $request );
    23             if ( ! file_exists( $files['zip'] ) ) {
    24                 $builder = new Builder( $request['slug'], $request['version'] );
    25                 $builder->build();
    26                 clearstatcache();
    27             }
    28 
    29             $this->serve_zip( $files, $request );
     19            $this->serve_zip( $request );
    3020
    3121            if ( $request['args']['stats'] ) {
     
    3424
    3525        } catch ( Exception $e )  {
    36             $this->error( $e->getCode() );
     26            $this->error();
    3727        }
    3828
     
    5141
    5242        if ( ! preg_match( "!^(?P<slug>[a-z0-9-]+)(.(?P<version>.+))?.zip$!i", $zip, $m ) ) {
    53             throw new Exception( __METHOD__ . ": Invalid URL" );
     43            throw new Exception( __METHOD__ . ": Invalid URL." );
    5444        }
    5545
     
    9686        }
    9787        if ( ! $version ) {
    98             throw new Exception( __METHOD__ . ": A version for $plugin_slug cannot be determined.", 404 );
     88            throw new Exception( __METHOD__ . ": A version for $plugin_slug cannot be determined." );
    9989        }
    10090
     
    121111
    122112        if ( ! $post_id ) {
    123             throw new Exception( __METHOD__ . ": A post_id for $plugin_slug cannot be determined.", 404 );
     113            throw new Exception( __METHOD__ . ": A post_id for $plugin_slug cannot be determined." );
    124114        }
    125115
     
    133123     * @return array An array containing the files to use for the request, 'zip' and 'md5'.
    134124     */
    135     protected function get_files( $request ) {
     125    protected function get_file( $request ) {
    136126        if ( empty( $request['version'] ) || 'trunk' == $request['version'] ) {
    137             $zip = self::ZIP_DIR . "/{$request['slug']}/{$request['slug']}.zip";
     127            return "{$request['slug']}/{$request['slug']}.zip";
    138128        } else {
    139             $zip = self::ZIP_DIR . "/{$request['slug']}/{$request['slug']}.{$request['version']}.zip";
    140         }
    141         $md5 = $zip . '.md5';
    142 
    143         return compact( 'zip', 'md5' );
     129            return "{$request['slug']}/{$request['slug']}.{$request['version']}.zip";
     130        }
    144131    }
    145132
     
    147134     * Output a ZIP file with all headers.
    148135     *
    149      * @param array $files {
    150      *   Array of files for the request.
    151      *
    152      *   @type string $zip The Zip file to serve.
    153      *   @type string $md5 The MD5 file to use for the Content-MD5 header. Optional.
    154      * }
    155136     * @param array $request The request array for the request.
    156137     */
    157     protected function serve_zip( $files, $request ) {
    158         header( 'Content-Type: application/zip' );
    159         header( 'Content-Disposition: attachment; filename=' . basename( $files['zip'] ) );
    160         if ( !empty( $files['md5'] ) && ( $md5 = file_get_contents( $files['md5'] ) )  ) {
    161             header( 'Content-MD5: ' . $md5 );
    162         }
    163 
    164         // TODO: Accel Redirect allows for ZIP files to be cached on the LB's
    165         // header('X-Accel-Redirect: ' . $accel_redirect );
    166 
    167         header( 'Content-Length: ' . filesize( $files['zip'] ) );
    168         readfile( $files['zip'] );
     138    protected function serve_zip( $request ) {
     139        $zip = $this->get_file( $request );
     140
     141        if ( defined( 'PLUGIN_ZIP_X_ACCEL_REDIRECT_LOCATION' ) ) {
     142            $zip_url = PLUGIN_ZIP_X_ACCEL_REDIRECT_LOCATION . $zip;
     143
     144            header( 'Content-Type: application/zip' );
     145            header( 'Content-Disposition: attachment; filename=' . basename( $zip ) );
     146            header( "X-Accel-Redirect: $zip_url" );
     147        } else {
     148            header( 'Content-Type: text/plain' );
     149            echo "This is a request for $zip, this server isn't currently configured to serve zip files.\n";
     150        }
     151
     152        if ( function_exists( 'fastcgi_finish_request' ) ) {
     153            fastcgi_finish_request();
     154        }
     155
    169156    }
    170157
     
    225212
    226213    /**
    227      * Quit with an Error code.
    228      *
    229      * @param int $code The HTTP Error code, 404 or 503.
    230      */
    231     protected function error( $code = 404 ) {
     214     * Bail with a 404.
     215     */
     216    protected function error() {
    232217        $protocol = isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
    233218        $protocol .= ' ';
    234         switch ( $code ) {
    235             case 503:
    236                 header( $protocol . '503 Service Unavailable' );
    237                 die( '503 Service Unavailable' );
    238 
    239             default:
    240             case 404:
    241                 header( $protocol . '404 File not found' );
    242                 die( '404 File not found' );
    243         }
     219
     220        header( $protocol . '404 File not found' );
     221        die( '404 file not found' );
    244222    }
    245223
Note: See TracChangeset for help on using the changeset viewer.