public function upgrade($upgrade_plugin = '') { $this->deprecationNotice(true); $this->whippet_init(); $this->load_plugins_manifest(); $this->load_plugins_lock(); // // 1. Find the plugin we're going to update. // 2. Check it out // 3. Update the lockfile // foreach ($this->plugins_manifest as $dir => $plugin) { // Upgrade the plugin if: // - It is the plugin they asked for // - They didn't specify a plugin, and this plugin is in the manifest. if ($dir == $upgrade_plugin || $upgrade_plugin == '' && isset($this->plugins_manifest->{$dir})) { $git = new \Dxw\Whippet\Git\Git("{$this->plugin_dir}/{$dir}"); // Find the specified revision. echo "[Checking {$dir}] "; $git->fetch(); // Check it out if (!$git->checkout($git->remote_revision_commit($plugin->revision))) { die; } if (!$git->submodule_update()) { die; } // If we were upgrading a specific plugin, bail now if ($upgrade_plugin != '') { break; } } } $this->update_plugins_lock(); }
function generate() { $old = $this->options->old; $new = $this->options->new; if (!file_exists($old)) { echo "Unable to find directory: {$old}\n"; } if (!file_exists($new)) { echo "Unable to find directory: {$new}\n"; } // TODO: sanity checks // Does $old look like a wp-content? // Does $new look like a whippet app? // Are they both git repos? $git = new \Dxw\Whippet\Git\Git($old); // // 1. Get a list of all the submodules. Add entries for any that are wordpress-plugins/whatever. // 2. Copy over all the themes // 3. Copy over all plugins that aren't submodules // * Ignore index.php // * Warn about plugin files that are not in a directory // * List all plugins copied into the app. // 4. Ignoring plugins, are there any other submodules? If so, recreated them (in the right place) // 5. Copy over whippet-wp-config.php, if it exists // 6. List all files that are not dealt with by the above, and prompt user to deal with them. // Don't worry about: // * uploads // * DS_Store // * ...? // 7. Summarise what happened to the plugins. For each: // * Was it put in Plugins or copied directly? // * Was it in its own directory? (can we do that automagically?) // * If it's copied directly, does a similar-looking repo exist? // * Is there an inspection for the plugin, and if so, what's the result? // // // Check that all submodules are well formed // echo "Loading submodules\n"; $submodules = $git->submodule_status(); foreach ($submodules as $submodule_dir => $submodule) { if (!empty($submodule->status)) { echo "Submodule {$submodule->dir} has unmerged changes, is uninitialised, or is not on the index commit. Aborting.\n"; exit(1); } } // // Fetch all the files, plugins and themes from the old project, and work out which are submoduled // echo "Loading plugins\n"; $plugins = $this->get_plugins("{$old}/plugins"); echo "Loading themes\n"; $themes = $this->get_themes("{$old}/themes"); echo "Loading everything else\n"; $other_files = $this->get_rogue_files($old, $plugins, $themes); // Find out which ones are submoduled // No need to worry about themes here, because they're not whippet managed. They're dealt with below. echo "Looking for Whippet manageable plugins\n"; foreach ($plugins as $plugin_file => $plugin_data) { foreach ($submodules as $submodule_dir => $submodule) { // If it looks like a theme, make a note of that for later if (dirname($submodule->dir) == "themes") { $submodule->theme_dir = basename($submodule->dir); } // From this point on, we only want plugins if (dirname($submodule->dir) != 'plugins' || dirname($plugin_file) != basename($submodule->dir)) { continue; } $plugins[$plugin_file]['is_submodule'] = isset($submodule->remotes['origin']) && preg_match('/^git@git\\.dxw\\.net:wordpress-plugins\\//', $submodule->remotes['origin']); $submodules[$submodule_dir]->plugin_file = $plugin_file; } } // // Add submoduled plugins to Plugins file. Remove those submodule & plugin entries. // // Always start with a fresh file // Whippet init adds akismet (as it is part of the WP distro) but we only want it now if it's in $old file_put_contents("{$new}/plugins", "source = \"git@git.dxw.net:wordpress-plugins/\"\n"); echo "Updating Plugins file\n"; foreach ($plugins as $plugin_file => $plugin_data) { if (!isset($plugin_data['is_submodule']) || !$plugin_data['is_submodule']) { continue; } $plugin_dir = dirname($plugin_file); file_put_contents("{$new}/plugins", "{$plugin_dir}=\n", FILE_APPEND); unset($plugins[$plugin_file]); unset($submodules["plugins/{$plugin_dir}"]); $this->automatic_fixes[] = "Added plugin {$plugin_dir} to the Plugins file"; } // // Check non-Whippet-managed plugins so we can warn if they are on directory or in auto-wordpress // $available_plugins = file(dirname(__FILE__) . "/share/available-plugins"); echo "Sanity-checking left over plugins\n"; foreach ($plugins as $plugin_file => $plugin_data) { if (array_search(dirname($plugin_file) . "\n", $available_plugins) !== false) { $this->manual_fixes[] = "Non-whippet-managed plugin is available in git: {$plugin_file}. Should it be Whippet-managed?"; } else { $plugin_slug = dirname($plugin_file); if ($plugin_slug == '.') { $plugin_slug = preg_replace("/\\.php\$/", '', $plugin_file); } $directory_headers = get_headers("http://www.wordpress.org/plugins/{$plugin_slug}/"); if (preg_match('/200 OK/', $directory_headers[0])) { $this->manual_fixes[] = "Non-whippet-managed plugin might be available on the Directory: " . dirname($plugin_file); } } } // // Re-add submodules that are left-over, and if any are plugins, remove the matching plugin entry // echo "Adding submodules\n"; foreach ($submodules as $dir => $submodule) { if (count($submodule->remotes['origin']) != 1) { $this->manual_fixes[] = "Skipped submodule {$submodule->dir}, because it does not have exactly 1 remote. You'll need to manually add the one you want."; unset($submodules[$dir]); continue; } $remote = array_pop($submodule->remotes); if (!empty($submodule->theme_dir)) { if (!$this->is_parent_theme($themes[$submodule->theme_dir]) && array_search($submodule->theme_dir, array("twentyten", "twentyeleven", "twentytwelve", "twentythirteen", "twentyfourteen")) !== false) { $this->manual_fixes[] = "Refusing to submodule a default theme from {$remote} at wp-content/{$submodule->dir}. Add a submodule for this theme manually if it is required."; unset($submodules[$dir]); unset($themes[$submodule->theme_dir]); continue; } else { if ($this->is_parent_theme($themes[$submodule->theme_dir])) { $this->manual_fixes[] = "Submoduled a default theme from {$remote} at wp-content/{$submodule->dir}, because it is a parent of: " . implode(', ', $themes[$submodule->theme_dir]['children']); } } } $git = new \Dxw\Whippet\Git\Git($new); if (!$git->submodule_add($remote, "wp-content/" . $submodule->dir)) { // This error will be added to manual fixes later, by seeing if anything is left over in $submodules. continue; } if (strpos($remote, "*****@*****.**") === false) { $this->manual_fixes[] = "Non-dxw git repo submoduled: {$remote} at {$submodule->dir}"; } if (isset($submodule->plugin_file)) { unset($plugins[$submodule->plugin_file]); $this->automatic_fixes[] = "Submoduled plugin from {$remote} at: wp-content/" . $submodule->dir; } else { if (isset($submodule->theme_dir)) { unset($themes[$submodule->theme_dir]); $this->automatic_fixes[] = "Submoduled theme from {$remote} at: wp-content/" . $submodule->dir; } else { $this->automatic_fixes[] = "Submodule added at: wp-content/" . $submodule->dir; } } unset($submodules[$dir]); } // // Copy over any plugins that are left over // echo "Copying project plugins\n"; foreach ($plugins as $plugin_file => $plugin_data) { if (dirname($plugin_file) != '.') { $this->recurse_copy("{$old}/plugins/" . dirname($plugin_file), "{$new}/wp-content/plugins/" . dirname($plugin_file)); $this->automatic_fixes[] = "Copied plugin directory " . dirname($plugin_file) . " into the project"; } else { $this->recurse_copy("{$old}/plugins/{$plugin_file}", "{$new}/wp-content/plugins"); $this->automatic_fixes[] = "Copied plugin file {$plugin_file} into the project"; } unset($plugins[$plugin_file]); } // // Copy over any themes that are left over // echo "Copying project themes\n"; foreach ($themes as $theme_dir => $theme_data) { if (!$this->is_parent_theme($theme_data) && array_search($theme_dir, array("twentyten", "twentyeleven", "twentytwelve", "twentythirteen", "twentyfourteen")) !== false) { $this->manual_fixes[] = "Refusing to copy a default theme into the project: {$theme_dir}. Copy it manually if you need it."; unset($themes[$theme_dir]); continue; } else { if ($this->is_parent_theme($theme_data)) { $this->manual_fixes[] = "Copied theme directory {$theme_dir} into the project, because it is a parent of: " . implode(', ', $theme_data['children']); } } $new_theme_dir = "{$new}/wp-content/themes/" . dirname($theme_dir); if (!file_exists($new_theme_dir)) { mkdir("{$new_theme_dir}", 0755, true); // For themes within subdirs } $this->recurse_copy("{$old}/themes/{$theme_dir}", "{$new_theme_dir}/{$theme_dir}"); $this->automatic_fixes[] = "Copied theme directory {$theme_dir} into the project"; unset($themes[$theme_dir]); } // // Check that we don't have any themes, plugins or submodules left over. // echo "Checking for unhandled plugins, themes and submodules\n"; // Make sure there's nothing left foreach ($plugins as $plugin_file => $plugin_data) { $this->manual_fixes[] = "Plugin {$plugin_file} was not migrated"; } foreach ($themes as $theme_dir => $theme_data) { $this->manual_fixes[] = "Plugin {$theme_dir} was not migrated"; } foreach ($submodules as $submodule_dir => $submodule) { $this->manual_fixes[] = "Submodule {$submodule_dir} was not migrated"; } // // If there are language files, copy them // if (file_exists("{$old}/languages")) { echo "Copying language files\n"; mkdir("{$new}/wp-content/languages/"); foreach (glob("{$old}/languages/*.mo") as $file) { copy($file, "{$new}/wp-content/languages/{$file}"); } $this->automatic_fixes[] = "Copied language directory into the project"; } // // What happened? // $results = ""; $results .= "\n\nApplied these automatic fixes:\n"; $results .= "==============================\n"; foreach ($this->automatic_fixes as $fix) { $results .= " {$fix}\n"; } $results .= "\n\nNotices/possible manual fixes:\n"; $results .= "==============================\n"; if (!count($this->manual_fixes)) { $results .= " None.\n"; } else { foreach ($this->manual_fixes as $fix) { $results .= " {$fix}\n"; } } $results .= "\n\nLeft-over files:\n"; $results .= "================\n"; if (!count($other_files)) { $results .= " None.\n"; } else { foreach ($other_files as $file) { $results .= " {$file}\n"; } } file_put_contents("{$new}/migration.log", $results); echo $results; }
/** * Updates plugins.lock based on the contents of the current plugins manifest. * * This method works because $this->plugins_manifest is updated as Whippet carries out plugin installations, updates and deletions. */ private function update_plugins_lock() { if (!empty($this->plugins_locked)) { $this->old_plugins_locked = $this->plugins_locked; } $this->plugins_lock_file = "{$this->project_dir}/plugins.lock"; $this->plugins_locked = new \stdClass(); foreach (scandir($this->plugin_dir) as $dir) { if ($dir[0] == '.') { continue; } if (!isset($this->plugins_manifest->{$dir})) { continue; } $git = new \Dxw\Whippet\Git\Git("{$this->plugin_dir}/{$dir}"); if (!($commit = $git->current_commit())) { echo "Unable to determine current commit; aborting\n"; exit(1); } $this->plugins_locked->{$dir} = new \stdClass(); $this->plugins_locked->{$dir}->repository = $this->plugins_manifest->{$dir}->repository; $this->plugins_locked->{$dir}->revision = $this->plugins_manifest->{$dir}->revision; $this->plugins_locked->{$dir}->commit = $commit; } return file_put_contents($this->plugins_lock_file, json_encode($this->plugins_locked, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); }
public function create(&$force) { // // Does this commit have a release directory already? If so, do nothing // if (!$force && file_exists($this->release_dir)) { return false; } // there's no point in forcing a non-existant release if ($force && !file_exists($this->release_dir)) { $force = false; } // Got whippet.{json,lock} or plugins.lock? if (is_file($this->project_dir . '/whippet.json') && is_file($this->project_dir . '/whippet.lock')) { $factory = new \Dxw\Whippet\Factory(); $installer = new \Dxw\Whippet\Dependencies\Installer($factory, new \Dxw\Whippet\ProjectDirectory($this->project_dir)); } elseif ($this->plugins_lock_file && file_exists($this->plugins_lock_file)) { $installer = new Plugin(); } else { echo "Couldn't find plugins.lock in the project directory. (Did you run whippet plugins install?)\n"; die(1); } // // If we're here, we must deploy // // 1. Clone WP // 2. Delete wp-content etc // 3. Make sure wp-content is up to date // 4. Copy our wp-content, omitting gitfoo // 5. ?? Theme/plugin build steps ?? (Makefile-esque thing?) // 6. Symlink required files from shared dir // Assuming we're not forcing, create a new directory for this release, or use only an empty existing dir if (!$force) { $this->check_and_create_dir($this->release_dir, true); } else { $this->release_dir = dirname($this->release_dir) . '/forced_release_tmp_' . sha1(microtime()); } // Clone WP and remove things we don't want $wp = new \Dxw\Whippet\Git\Git($this->release_dir); $wp->clone_repo($this->application_config->wordpress->repository); $wp->checkout($this->application_config->wordpress->revision); foreach (['wp-content', '.git', 'readme.html', 'wp-config-sample.php'] as $delete) { if (is_dir("{$this->release_dir}/{$delete}")) { $this->recurse_rmdir("{$this->release_dir}/{$delete}"); } else { unlink("{$this->release_dir}/{$delete}"); } } // Make sure wp-content is up to date $result = $installer->install(true); if ($result->isErr()) { echo sprintf("ERROR: %s\n", $result->getErr()); exit(1); } // Copy over wp-content $this->recurse_copy("{$this->project_dir}/wp-content", "{$this->release_dir}/wp-content"); if (file_exists("{$this->release_dir}/wp-content/uploads")) { $this->recurse_rm("{$this->release_dir}/wp-content/uploads"); } // // Remove unwanted git/test foo // $plugins = scandir("{$this->release_dir}/wp-content/plugins"); foreach ($plugins as $dir) { $path = "{$this->release_dir}/wp-content/plugins/{$dir}"; if ($dir === '.' || $dir === '..' || !is_dir($path)) { continue; } // Remove git files from all plugins foreach (['.git', '.gitmodules', '.gitignore'] as $delete) { $this->recurse_rm("{$this->release_dir}/wp-content/plugins/{$dir}/{$delete}"); } // Remove test files from whippet plugins if ($this->is_whippet_plugin($path)) { foreach (['tests', 'Makefile', '.drone.yml'] as $delete) { $this->recurse_rm("{$this->release_dir}/wp-content/plugins/{$dir}/{$delete}"); } } } // // Copy public assets // if (is_dir("{$this->project_dir}/public")) { $this->recurse_copy("{$this->project_dir}/public", "{$this->release_dir}"); } // // TODO: theme and plugin build steps // // Symlinkery symlink(realpath("{$this->release_dir}/../../shared/wp-config.php"), "{$this->release_dir}/wp-config.php"); symlink(realpath("{$this->release_dir}/../../shared/uploads"), "{$this->release_dir}/wp-content/uploads"); // FIN }