{"id":1015,"date":"2008-11-26T08:23:49","date_gmt":"2008-11-26T15:23:49","guid":{"rendered":"http:\/\/www.gubatron.com\/blog\/?p=1015"},"modified":"2008-11-26T08:23:49","modified_gmt":"2008-11-26T15:23:49","slug":"python-script-to-update-wordpress-in-one-step","status":"publish","type":"post","link":"https:\/\/www.gubatron.com\/blog\/python-script-to-update-wordpress-in-one-step\/","title":{"rendered":"Python Script to Update WordPress in One Step"},"content":{"rendered":"<p>During the past week, I think I had to update all my wordpress instances twice, and it&#8217;s become really annoying doing this manually. I&#8217;ve written a python script which I&#8217;ll share with you.<\/p>\n<p><strong>How I keep my wordpress updated by hand<\/strong><br \/>\nI tend to keep my wp-content folder outside of my wordpress installation for 2 reasons:<\/p>\n<p>1. I don&#8217;t like to loose my themes, plugins and customizations<br \/>\n2. I like to keep all my customization changes under subversion<\/p>\n<p>So, if I had my wordpress installation say at:<br \/>\n<strong>\/home\/user\/public_html\/blog<\/strong><\/p>\n<p>I&#8217;d keep my wp-content folder for that here:<\/p>\n<p><strong>\/home\/user\/public_html\/wp-content-for-blog<\/strong><\/p>\n<p>So when I upgrade my blog, I always remove the original wp-content folder that comes along wordpress, and I symlink my hard worked on wp-content folder that lives outside to the freshly unzipped wordpress folder.<\/p>\n<p>[bash]<br \/>\nuser@machine:~\/public_html\/blog$ ls -l<br \/>\n&#8230;<br \/>\nlrwxrwxr-x 1 user www    54 2008-11-26 09:29 wp-content -&gt; \/home\/user\/public_html\/wp-content-for-blog<br \/>\n&#8230;<br \/>\n[\/bash]<\/p>\n<p>So what I endup doing all the time, is downloading the <a href=\"http:\/\/www.wordpress.org\/latest.zip\" rel=\"nofollow\">latest.zip<\/a> to ~\/public_html\/, it will unzip under ~\/public_html\/wordpress, and then I&#8217;ll copy the current ~\/public_html\/blog\/wp-config.php to ~\/public_html\/wordpress, then I&#8217;ll remove the default ~\/public_html\/wordpress\/wp-content and symlink the outer wp-content with all my customizations, themes and plugins to it. Once done, I&#8217;ll make a backup of the old wordpress folder, and then I&#8217;ll rename wordpress folder to the name of the blog folder, and it&#8217;s all done.<\/p>\n<p>It&#8217;s simple, but when you have to do it for 5 blogs, every week, it&#8217;s not fun anymore.<\/p>\n<p><strong>The Update Script<\/strong><\/p>\n<p>So here&#8217;s a script to do it in one step. If you&#8217;re not using my symlinked technique, this will do it for you, you only need to specify the full path to the folder where you want to keep your current wp-content folder outside the new installation before you apply the update, and the name of the folder where your current blog lives. The script below will have its configuration variables towards the beginning set so that they are in line with the example I&#8217;ve been talking about.<\/p>\n<p>[python]<br \/>\n#!\/usr\/bin\/python<br \/>\n#########################################################################################<br \/>\n#<br \/>\n# upgrade_wordpress.py &#8211; Script to automatically upgrade your wordpress installation.<br \/>\n#<br \/>\n# Requirements:<br \/>\n#   &#8211; Python 2.4 or older<br \/>\n#   &#8211; WordPress should already be installed<br \/>\n#   &#8211; CURL (sudo apt-get install curl)<br \/>\n#<br \/>\n# Author: Angel (Gubatron) Leon<br \/>\n# LICENSE: See the GPL2 license.<br \/>\n# 2008<br \/>\n#########################################################################################<br \/>\nimport os<\/p>\n<p>#########################################################################################<br \/>\n#Config (relative to the folder where this script will be run from)<br \/>\n#########################################################################################<\/p>\n<p>#The current folder where the blog lives<br \/>\nBLOG_FOLDER=&#8217;blog&#8217;<\/p>\n<p>#<br \/>\n# The first time you run the script, it will try to make a copy of your<br \/>\n# current wp-content folder outside. Copy here the location of where<br \/>\n# the wp-content folder with your themes and plugins should exist.<br \/>\n#<br \/>\n# After it unzips, it will remove the default wp-content folder from<br \/>\n# the new installation, and it will symlink the external wp-content<br \/>\n# That way you don&#8217;t ever have to worry about loosing your customizations<br \/>\n# and plugins.<br \/>\n#<br \/>\nWP_CONTENT_OUTSIDE_COPY_FOLDER=&quot;\/home\/user\/public_html\/wp-content-for-blog&quot;<\/p>\n<p>#This is where a backup of your current blog will be<br \/>\nBLOG_FOLDER_BACKUP_FOLDER=BLOG_FOLDER+&#8217;.old&#8217;<\/p>\n<p>#Where to download the wordpress latest.zip from<br \/>\nWORDPRESS_LATEST_ZIP_URL=&#8217;http:\/\/wordpress.org\/latest.zip&#8217;<\/p>\n<p>#### DO NOT MODIFY AFTER THESE LINES ####<\/p>\n<p>def downloadWordpress(url=WORDPRESS_LATEST_ZIP_URL):<br \/>\n    if os.path.exists(&#8216;latest.zip&#8217;):<br \/>\n        print &quot;Removing old latest.zip&quot;<br \/>\n        os.remove(&#8216;latest.zip&#8217;)<\/p>\n<p>    #Try to download with CURL<br \/>\n    print &quot;Attempting to download latest.zip from wordpress.org&quot;<br \/>\n    os.system(&#8216;curl %s -o latest.zip&#8217; % url)<\/p>\n<p>    if not os.path.exists(&#8216;latest.zip&#8217;):<br \/>\n        os.system(&#8216;wget &#8216; + url)<\/p>\n<p>    return os.path.exists(&#8216;latest.zip&#8217;)<\/p>\n<p>def dirExists(dirName):<br \/>\n    return os.path.exists(dirName) and os.path.isdir(dirName)<\/p>\n<p>def backupBlog(currentBlogFolder=BLOG_FOLDER,<br \/>\n               wpContentOriginalFolder=WP_CONTENT_OUTSIDE_COPY_FOLDER,<br \/>\n               backupFolder=BLOG_FOLDER_BACKUP_FOLDER):<\/p>\n<p>    #Remove any previous backups<br \/>\n    if os.path.exists(backupFolder) and os.path.isdir(backupFolder):<br \/>\n        print &quot;Removing previous backup folder&quot;<br \/>\n        os.system(&#8216;rm -fr &#8216; + backupFolder)<\/p>\n<p>    #Copy the current blog folder into a backup folder just in case.<br \/>\n    #We won&#8217;t do any database backups for now.<br \/>\n    print &quot;Creating new backup folder&quot;<br \/>\n    os.system(&#8216;cp -r %s %s&#8217; % (currentBlogFolder,backupFolder))<\/p>\n<p>    #Check for the copy of wp-content outside the blog, if it doesn&#8217;t exist<br \/>\n    #we&#8217;ll make it for the first time.<br \/>\n    if not dirExists(wpContentOriginalFolder):<br \/>\n        print &quot;Creating outside copy of wp-content&quot;<br \/>\n        os.system(&#8216;cp -r %s %s&#8217; % (os.path.join(currentBlogFolder,&#8217;wp-content&#8217;),<br \/>\n                                   wpContentOriginalFolder))<\/p>\n<p>    #Copy the latest wp-config.php outside to the current folder<br \/>\n    print &quot;Copying your latest wp-config.php outside&quot;<br \/>\n    os.system(&#8216;cp %s .&#8217; % (os.path.join(currentBlogFolder,&#8217;wp-config.php&#8217;)))<\/p>\n<p>    backupFolderExists = dirExists(backupFolder)<br \/>\n    wpContentFolderExists = dirExists(wpContentOriginalFolder)<br \/>\n    configFileExists = os.path.exists(&#8216;wp-config.php&#8217;)<\/p>\n<p>    return backupFolderExists and wpContentOriginalFolder and configFileExists<\/p>\n<p>def upgradeBlog(currentBlogFolder=BLOG_FOLDER,<br \/>\n                backupFolder=BLOG_FOLDER_BACKUP_FOLDER,<br \/>\n                url=WORDPRESS_LATEST_ZIP_URL,<br \/>\n                wpContentOriginalFolder=WP_CONTENT_OUTSIDE_COPY_FOLDER):<\/p>\n<p>    if not downloadWordpress(url):<br \/>\n        print &quot;Could not download latest.zip, aborting.&quot;<br \/>\n        return False<\/p>\n<p>    if not backupBlog(currentBlogFolder,wpContentOriginalFolder,backupFolder):<br \/>\n        print &quot;Could not backup blog or wp-config.ph, aborting.&quot;<br \/>\n        return False<\/p>\n<p>    if currentBlogFolder == &#8216;wordpress&#8217;:<br \/>\n        print &quot;The current blog folder cannot be &#8216;wordpress, aborting.&quot;<br \/>\n        return False<\/p>\n<p>    #1. If a wordpress\/ folder exists, wipe it.<br \/>\n    if dirExists(&#8216;wordpress&#8217;):<br \/>\n        print &quot;Removing old wordpress folder&quot;<br \/>\n        os.system(&#8216;rm -fr wordpress&#8217;)<\/p>\n<p>    if dirExists(&#8216;%s.delete&#8217; % currentBlogFolder):<br \/>\n        print &quot;Removing old %s.delete folder&quot; % currentBlogFolder<br \/>\n        os.system(&#8216;rm -fr %s.delete folder&#8217; % currentBlogFolder)<\/p>\n<p>    #2. Unzip new copy<br \/>\n    os.system(&#8216;unzip latest.zip&#8217;)<\/p>\n<p>    if not dirExists(&#8216;wordpress&#8217;):<br \/>\n        print &quot;Could not unzip the wordpress installation, aborting.&quot;<br \/>\n        return False<\/p>\n<p>    #1. Copy wp-config.php into the new installation<br \/>\n    os.system(&#8216;cp wp-config.php wordpress\/&#8217;)<\/p>\n<p>    #2. Remove the default wp-content folder<br \/>\n    os.system(&#8216;rm -fr wordpress\/wp-content&#8217;)<\/p>\n<p>    #3. Symlink the original wp-content that lives outside<br \/>\n    os.system(&#8216;ln -s %s wordpress\/wp-content&#8217; % (wpContentOriginalFolder))<\/p>\n<p>    #4. Verify symlink was created<br \/>\n    if not (os.path.exists(&#8216;wordpress\/wp-content&#8217;) and os.path.islink(&#8216;wordpress\/wp-content&#8217;)):<br \/>\n        print &quot;Could not create symlink to wp-content, aborting.&quot;<br \/>\n        return False<\/p>\n<p>    #5. Move original folder to folder.delete, and make this wordpress folder the current folder.<br \/>\n    os.system(&#8216;mv %s %s.delete&#8217; % (currentBlogFolder,currentBlogFolder))<\/p>\n<p>    if not dirExists(currentBlogFolder + &quot;.delete&quot;):<br \/>\n        print &quot;Could not rename current folder for later deletion, aborting.&quot;<br \/>\n        return False<\/p>\n<p>    #6. Rename the new installation as the current blog<br \/>\n    os.system(&#8216;mv %s %s&#8217; % (&#8216;wordpress&#8217;,currentBlogFolder))<\/p>\n<p>    if dirExists(&#8216;wordpress&#8217;):<br \/>\n        print &quot;ALERT: The wordpress folder still exists.&quot;<br \/>\n        return False<\/p>\n<p>    if not dirExists(currentBlogFolder):<br \/>\n        print &quot;ALERT: The blog doesn&#8217;t exist, recover from the backup folder %s please&quot; % (backupFolder)<br \/>\n        return False<\/p>\n<p>    #7 Cleanup<br \/>\n    os.system(&#8216;rm -fr %s.delete&#8217; % (currentBlogFolder))<\/p>\n<p>    return True<\/p>\n<p>if __name__ == &#8216;__main__&#8217;:<br \/>\n    upgradeBlog()<br \/>\n[\/python]<\/p>\n<p><strong>Requirements<\/strong><\/p>\n<li>shell access to the machine where you have your wordpress installed<\/li>\n<li>a python interpreter installed<\/li>\n<li>curl (sudo apt-get install curl) to download the zip. If you don&#8217;t have it it&#8217;ll attempt to use wget<\/li>\n<p><strong>Installation<\/strong><\/p>\n<li>Right outside your wordpress installation folder, create a new file called <strong>upgrade_wordpress.py<\/strong><\/li>\n<li>Copy and paste the script inside that file<\/li>\n<li>Edit the configuration variables to point to the name of your wordpress installation folder, and give it a full path to where you want to keep your wp-content folder (including the name of the folder, so if you want to name it the same way, you could do for example \/home\/user\/wp-content and it&#8217;ll be saved right under your home)<\/li>\n<p><strong>Usage:<\/strong><br \/>\n[bash]python upgrade_wordpress.py[\/bash]<\/p>\n<p>The script is very fault proof, it will always try to abort in case something is not going the way it&#8217;s expected. At the end of the day it&#8217;ll also leave a backup copy of your current blog in case something goes bad, you can always recover.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>During the past week, I think I had to update all my wordpress instances twice, and it&#8217;s become really annoying doing this manually. I&#8217;ve written a python script which I&#8217;ll share with you. How I keep my wordpress updated by hand I tend to keep my wp-content folder outside of my wordpress installation for 2 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[15,65],"tags":[166,738,1458,1117],"class_list":["post-1015","post","type-post","status-publish","format-standard","hentry","category-code","category-python","tag-automation","tag-one-step","tag-python","tag-wordpress"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p5Unzf-gn","jetpack-related-posts":[{"id":532,"url":"https:\/\/www.gubatron.com\/blog\/how-to-update-file-timestamps-in-python\/","url_meta":{"origin":1015,"position":0},"title":"How to update file timestamps in Python","author":"gubatron","date":"May 29, 2007","format":false,"excerpt":"Sometimes you can be real picky like me about timestamps of files, for example, during my wedding we had a few digital cameras, and one of the cameras had its internal clock 4 hours behind. So what better way for a lazy guy like you to change timestamps than writing\u2026","rel":"","context":"In &quot;Code&quot;","block_context":{"text":"Code","link":"https:\/\/www.gubatron.com\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":273,"url":"https:\/\/www.gubatron.com\/blog\/snowrss\/","url_meta":{"origin":1015,"position":1},"title":"SnowRSS","author":"gubatron","date":"March 18, 2006","format":false,"excerpt":"SnowRSS SnowRSS is an RSS Aggregator engine I wrote in python (Licensed under the GPL). Currently it's been under use in wedoit4you.com and its stable. It can read RSS and ATOM feeds. It uses the feedparser python module, and the MySQLdb python module to do the job. DOWNLOAD You can\u2026","rel":"","context":"In &quot;Gubatron&quot;","block_context":{"text":"Gubatron","link":"https:\/\/www.gubatron.com\/blog\/category\/gubatron\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":272,"url":"https:\/\/www.gubatron.com\/blog\/project-snowrss\/","url_meta":{"origin":1015,"position":2},"title":"Project SnowRSS","author":"gubatron","date":"March 18, 2006","format":false,"excerpt":"SnowRSS is a GPL RSS Aggregator engine I wrote in python. Currently it's been under use in wedoit4you.com and its stable. It can read RSS and ATOM feeds. It uses the feedparser python module, and the MySQLdb python module to do the job. DOWNLOAD You can only download the code\u2026","rel":"","context":"In &quot;Gubatron&quot;","block_context":{"text":"Gubatron","link":"https:\/\/www.gubatron.com\/blog\/category\/gubatron\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":572,"url":"https:\/\/www.gubatron.com\/blog\/list-dir-by-gubatron-list-your-directories-on-a-text-file-with-a-right-click\/","url_meta":{"origin":1015,"position":3},"title":"List Dir by Gubatron &#8211; List your directories on a text file with a right click","author":"gubatron","date":"August 12, 2007","format":false,"excerpt":"Last week my wife told me if I knew an easy way (without using the cmd.exe) on Windows to list the contents of a directory, she basically wanted to give her users a one click solution to list the contents of a folder and have them on notepad. So, I\u2026","rel":"","context":"In &quot;Code&quot;","block_context":{"text":"Code","link":"https:\/\/www.gubatron.com\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2749,"url":"https:\/\/www.gubatron.com\/blog\/deploying-html5-apps-on-cloudfront-with-efficient-invalidation-requests\/","url_meta":{"origin":1015,"position":4},"title":"Deploying HTML5 apps on CloudFront with efficient invalidation requests","author":"gubatron","date":"June 8, 2012","format":false,"excerpt":"So you decided to build your next web app\/site using nothing but HTML5 and Javascript. No server side processing for anything related to UI. This means you will be coding a lot of JavaScript. Wouldn't it be nice to put all that static HTML and JS on your CloudFront CDN\u2026","rel":"","context":"In &quot;Code&quot;","block_context":{"text":"Code","link":"https:\/\/www.gubatron.com\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":241,"url":"https:\/\/www.gubatron.com\/blog\/flirting-with-python-a-pastebincom-plugin-for-xchat\/","url_meta":{"origin":1015,"position":5},"title":"Flirting with Python &#8211; A pastebin.com plugin for xchat","author":"gubatron","date":"January 20, 2006","format":false,"excerpt":"Here's the first version of an xchat plugin some friends from Venezuela and I wrote. This is one of our first attempts to code something useful in python that'll we hope gets to be used by someone on the opensource community who often uses pastebin.com to show their code and\u2026","rel":"","context":"In &quot;Gubatron&quot;","block_context":{"text":"Gubatron","link":"https:\/\/www.gubatron.com\/blog\/category\/gubatron\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/posts\/1015","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/comments?post=1015"}],"version-history":[{"count":0,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/posts\/1015\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/media?parent=1015"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/categories?post=1015"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gubatron.com\/blog\/wp-json\/wp\/v2\/tags?post=1015"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}