Skip to content

Instantly share code, notes, and snippets.

@mikhail
Created August 27, 2015 15:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikhail/430ad49e1fa6061a67c6 to your computer and use it in GitHub Desktop.
Save mikhail/430ad49e1fa6061a67c6 to your computer and use it in GitHub Desktop.
Git -> WordPress draft publisher
#!/usr/bin/env python
import logging
import md5
import optparse
import os
import re
import subprocess
import sys
from wordpress_xmlrpc import Client, WordPressPost
from wordpress_xmlrpc.methods.posts import GetPost, GetPosts, NewPost, EditPost
import markdown
import xmlrpclib
import yaml
"""Create new Wordpress Post from meta.yml files.
Recursively walk the current_dir directory looking for meta.yml
When found - check the specified `slug` and create a post if it's not created.
"""
log = logging.getLogger('WPPost')
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
META_FILE = 'meta.yaml'
# Initial option handler to set up the basic application environment.
usage = 'usage: %prog <options>'
parser = optparse.OptionParser(usage=usage, add_help_option=True)
parser.set_defaults(verbose=True)
# Job Configuration
parser.add_option('-d', '--dry', dest='dry', action='store_true',
help='Executes a dry run only.')
# Logging Configuration
parser.add_option('-l', '--level', dest='level', default='info',
help='Set logging level (INFO|WARN|DEBUG|ERROR)')
parser.add_option('', '--debug', dest='level_debug', default=False,
action='store_true', help='Equivalent to --level=DEBUG')
(options, args) = parser.parse_args()
def get_hashsum(directory):
"""Get a unique hash for the entire directory."""
total_bits = subprocess.Popen(
'tar c --exclude .git --exclude "*.log" %s' % directory,
shell=True,
stdout=subprocess.PIPE).stdout.read()
return md5.new(total_bits).hexdigest()
def run():
log.info('Connecting to WordPress RPC...')
wp = Client('https://engblog.nextdoor.com/xmlrpc.php',
os.getenv('WP_USER'),
os.getenv('WP_PASS'))
try:
all_posts = wp.call(GetPosts())
except xmlrpclib.Fault as e:
log.critical('Could not fetch WP posts. Reason: %s' % e)
return False
log.info('Success!')
statuses = dict([(p.slug, p) for p in all_posts])
log.info('Finding meta files')
exit_status = 0
for current_dir, dirs, files in os.walk('.'):
if META_FILE not in files:
continue
log.debug('Found meta file in %s' % current_dir)
meta = yaml.load(open('%s/%s' % (current_dir, META_FILE)).read())
log.debug('Checking content file...')
content_file = '%s/%s' % (current_dir, meta['contents'])
try:
md_content = open(content_file).read()
except IOError as e:
log.error('Path %s cannot be opened. Reason: %s' % (
content_file, e))
exit_status = exit_status + 1
continue
slug = meta['slug']
orig_post = statuses.get(slug)
status = None
if orig_post:
status = orig_post.post_status
if status == 'publish':
log.info('Slug "%s" is already published. Skipping' % slug)
continue
if status == 'draft':
log.warning('Slug "%s" exists. Editing it.' % slug)
post = orig_post
elif status is None:
log.info('Creating a WP Post for "%s"' % slug)
post = WordPressPost()
post.custom_fields = [{'key': 'cpa_author',
'value': meta['author']}]
metaversion = get_hashsum(current_dir)
online_version = [str(pair['value'])
for pair in post.custom_fields
if pair['key'] == 'version']
if len(online_version) == 1 and online_version[0] == metaversion:
log.info('Meta version not updated. Skipping.')
continue
log.info('Version changed to %s. Updating.' % metaversion)
# Update the "version" field
for f in post.custom_fields:
if f['key'] == 'version':
f['value'] = metaversion
break
else:
post.custom_fields.extend([{'key': 'version', 'value': metaversion}])
post.title = meta['title']
post.excerpt = meta['summary']
# Custom Post Author plugin
post.terms_names = { 'post_tag': meta['tags'] }
post.slug = slug
# 1) Escape non-unicode. 2) Convert from Markdown 3) Unescape non-unicode
html_content = markdown.markdown(
md_content.encode('string-escape').replace(r'\n', '\n')
).decode('string-escape')
# Remove single new lines
minified_html = re.sub("([^\n])\n([^\n])", r'\1 \2', html_content)
# Convert more than 2 new lines into just 2
minified_html = re.sub('\n{3,}', '\n\n', minified_html)
post.content = minified_html
log.info('Uploading post "%s" to WordPress...' % slug)
log.debug('Checking if post needs to be created or updated.')
log.debug(post.__dict__)
if hasattr(post, 'id'):
log.info('Updating...')
if not options.dry:
wp.call(EditPost(post.id, post))
else:
log.warning('Dry run. Not doing anything.')
log.info('Done!')
else:
log.info('POSTING NEW BLOG!')
if not options.dry:
pid = wp.call(NewPost(post))
log.debug('Fetching post %s' % pid)
post = wp.call(GetPost(pid))
log.info('Created %s' % post.guid)
else:
log.warning('Dry run. Did not do anything.')
exit(exit_status)
if __name__ == '__main__':
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment