Skip to content

Commit

Permalink
Merge 322aa48 into 8f305c5
Browse files Browse the repository at this point in the history
  • Loading branch information
punchagan committed Apr 21, 2014
2 parents 8f305c5 + 322aa48 commit b072c4e
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 28 deletions.
5 changes: 5 additions & 0 deletions nikola/conf.py.in
Expand Up @@ -225,6 +225,11 @@ REDIRECTIONS = ${REDIRECTIONS}
# To do manual deployment, set it to []
# DEPLOY_COMMANDS = []

# Branches to use for the GitHub deploy.
# For user/organization pages, the deploy branch MUST be master.
GITHUB_SOURCE_BRANCH = 'master'
GITHUB_DEPLOY_BRANCH = 'gh-pages'

# Where the output site should be located
# If you don't use an absolute path, it will be considered as relative
# to the location of conf.py
Expand Down
9 changes: 9 additions & 0 deletions nikola/plugins/command/github_deploy.plugin
@@ -0,0 +1,9 @@
[Core]
Name = github_deploy
Module = github_deploy

[Documentation]
Author = Puneeth Chaganti
Version = 0.1
Website = http://getnikola.com
Description = Deploy the site to GitHub pages.
199 changes: 199 additions & 0 deletions nikola/plugins/command/github_deploy.py
@@ -0,0 +1,199 @@
# -*- coding: utf-8 -*-

# Copyright © 2014 Puneeth Chaganti and others.

# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the
# Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the
# Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice
# shall be included in all copies or substantial portions of
# the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from __future__ import print_function
import os
import shutil
import subprocess
import sys

from nikola.plugin_categories import Command
from nikola.plugins.command.check import real_scan_files
from nikola.utils import ask_yesno, get_logger
from nikola.__main__ import main
from nikola import __version__


class CommandGitHubDeploy(Command):
""" Deploy site to GitHub pages. """
name = 'github_deploy'

doc_usage = ''
doc_purpose = 'deploy the site to GitHub pages'

logger = None

_deploy_branch = ''
_source_branch = ''

def _execute(self, command, args):

self.logger = get_logger(
CommandGitHubDeploy.name, self.site.loghandlers
)
self._source_branch = self.site.config.get(
'GITHUB_SOURCE_BRANCH', 'master'
)
self._deploy_branch = self.site.config.get(
'GITHUB_DEPLOY_BRANCH', 'gh-pages'
)

self._ensure_git_repo()

message = (
"Make sure you have all source files committed. Anything not "
"committed, and unknown to Nikola may be lost. Continue? "
)

if not ask_yesno(message, False):
return

build = main(['build'])
if build != 0:
self.logger.error('Build failed, not deploying to GitHub')
sys.exit(build)

only_on_output, _ = real_scan_files(self.site)
for f in only_on_output:
os.unlink(f)

self._checkout_deploy_branch()

self._copy_output()

self._commit_and_push()

return

def _commit_and_push(self):
""" Commit all the files and push. """

deploy = self._deploy_branch
source = self._source_branch

source_commit = subprocess.check_output(['git', 'rev-parse', source])
commit_message = (
'Nikola auto commit.\n\n'
'Source commit: %s'
'Nikola version: %s' % (source_commit, __version__)
)

commands = [
['git', 'add', '-A'],
['git', 'commit', '-m', '%s' % commit_message],
['git', 'push', 'origin', '%s:%s' % (deploy, deploy)],
['git', 'checkout', '%s' % source],
]

for command in commands:
self.logger.info("==> {0}".format(command))
try:
subprocess.check_call(command)
except subprocess.CalledProcessError as e:
self.logger.error(
'Failed GitHub deployment — command {0} '
'returned {1}'.format(e.cmd, e.returncode)
)
sys.exit(e.returncode)

def _copy_output(self):
""" Copy all output to the top level directory. """
output_folder = self.site.config['OUTPUT_FOLDER']
for each in os.listdir(output_folder):
if os.path.exists(each):
if os.path.isdir(each):
shutil.rmtree(each)

else:
os.unlink(each)

shutil.move(os.path.join(output_folder, each), '.')

def _checkout_deploy_branch(self):
""" Check out the deploy branch
Creates an orphan branch if not present.
"""

deploy = self._deploy_branch

try:
command = 'git show-ref --verify --quiet refs/heads/%s' % deploy
subprocess.check_call(command.split())
except subprocess.CalledProcessError:
self._create_orphan_deploy_branch()
else:
command = 'git checkout %s' % deploy
subprocess.check_call(command.split())

def _create_orphan_deploy_branch(self):
command = 'git checkout --orphan %s' % self._deploy_branch
result = subprocess.check_call(command.split())
if result != 0:
self.logger.error('Failed to create a deploy branch')
sys.exit(1)

result = subprocess.check_call('git rm -rf .'.split())
if result != 0:
self.logger.error('Failed to create a deploy branch')
sys.exit(1)

with open('.gitignore', 'w') as f:
f.write('%s\n' % self.site.config['OUTPUT_FOLDER'])
f.write('%s\n' % self.site.config['CACHE_FOLDER'])
f.write('*.pyc\n')
f.write('*.db\n')

subprocess.check_call('git add .gitignore'.split())
subprocess.check_call(['git', 'commit', '-m', 'Add .gitignore'])

def _ensure_git_repo(self):
""" Ensure that the site is a git-repo.
Also make sure that a remote with the name 'origin' exists.
"""

try:
command = 'git remote'
remotes = subprocess.check_output(command.split())

except subprocess.CalledProcessError as e:
self.logger.notice('github_deploy needs a git repository!')
sys.exit(e.returncode)

except OSError as e:
import errno
self.logger.error('Running git failed with {0}'.format(e))
if e.errno == errno.ENOENT:
self.logger.notice('Is git on the PATH?')
sys.exit(1)

else:
if 'origin' not in remotes:
self.logger.error('Need a remote called "origin" configured')
sys.exit(1)
30 changes: 3 additions & 27 deletions nikola/plugins/command/init.py
Expand Up @@ -26,23 +26,23 @@

from __future__ import print_function
import os
import sys
import shutil
import codecs
import json
import textwrap
import datetime
import dateutil.tz

import dateutil.tz
from mako.template import Template
from pkg_resources import resource_filename

import nikola
from nikola.nikola import DEFAULT_TRANSLATIONS_PATTERN, DEFAULT_INDEX_READ_MORE_LINK, DEFAULT_RSS_READ_MORE_LINK, LEGAL_VALUES
from nikola.plugin_categories import Command
from nikola.utils import get_logger, makedirs, STDERR_HANDLER, load_messages
from nikola.utils import ask, ask_yesno, get_logger, makedirs, STDERR_HANDLER, load_messages
from nikola.winutils import fix_git_symlinked


LOGGER = get_logger('init', STDERR_HANDLER)

SAMPLE_CONF = {
Expand Down Expand Up @@ -254,30 +254,6 @@ def create_empty_site(cls, target):
@staticmethod
def ask_questions(target):
"""Ask some questions about Nikola."""
def ask(query, default):
"""Ask a question."""
if default:
default_q = ' [{0}]'.format(default)
else:
default_q = ''
inpf = raw_input if sys.version_info[0] == 2 else input
inp = inpf("{query}{default_q}: ".format(query=query, default_q=default_q)).strip()
return inp if inp else default

def ask_yesno(query, default=None):
if default is None:
default_q = ' [y/n]'
elif default is True:
default_q = ' [Y/n]'
elif default is False:
default_q = ' [y/N]'
inpf = raw_input if sys.version_info[0] == 2 else input
inp = inpf("{query}{default_q} ".format(query=query, default_q=default_q)).strip()
if inp:
return inp.lower().startswith('y')
else:
return default

def lhandler(default, toconf, show_header=True):
if show_header:
print("We will now ask you to provide the list of languages you want to use.")
Expand Down
35 changes: 34 additions & 1 deletion nikola/utils.py
Expand Up @@ -145,6 +145,7 @@ def req_missing(names, purpose, python=True, optional=False):
bytes_str = bytes
unicode_str = str
unichr = chr
raw_input = input
from imp import reload as _reload
else:
bytes_str = str
Expand All @@ -164,7 +165,8 @@ def req_missing(names, purpose, python=True, optional=False):
'_reload', 'unicode_str', 'bytes_str', 'unichr', 'Functionary',
'TranslatableSetting', 'TemplateHookRegistry', 'LocaleBorg',
'sys_encode', 'sys_decode', 'makedirs', 'get_parent_theme_name',
'demote_headers', 'get_translation_candidate', 'write_metadata']
'demote_headers', 'get_translation_candidate', 'write_metadata',
'ask', 'ask_yesno']

# Are you looking for 'generic_rss_renderer'?
# It's defined in nikola.nikola.Nikola (the site object).
Expand Down Expand Up @@ -1144,3 +1146,34 @@ def write_metadata(data):
meta.append('')

return '\n'.join(meta)


def ask(query, default=None):
"""Ask a question."""
if default:
default_q = ' [{0}]'.format(default)
else:
default_q = ''
inp = raw_input("{query}{default_q}: ".format(query=query, default_q=default_q)).strip()
if inp or default is None:
return inp
else:
return default


def ask_yesno(query, default=None):
"""Ask a yes/no question."""
if default is None:
default_q = ' [y/n]'
elif default is True:
default_q = ' [Y/n]'
elif default is False:
default_q = ' [y/N]'
inp = raw_input("{query}{default_q} ".format(query=query, default_q=default_q)).strip()
if inp:
return inp.lower().startswith('y')
elif default is not None:
return default
else:
# Loop if no answer and no default.
return ask_yesno(query, default)

0 comments on commit b072c4e

Please sign in to comment.