Created
October 19, 2009 22:50
-
-
Save wtnabe/213791 to your computer and use it in GitHub Desktop.
A Rakefile for simple web site creation and maintenance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- mode: ruby; coding: utf-8 -*- | |
# | |
# Require: | |
# * xoxo gem | |
# * lftp binary | |
# * linkchecker binary | |
# * html tidy binary | |
# * svn or hg or git | |
# * htpasswd binary | |
# | |
require 'uri' | |
require 'yaml' | |
require 'open3' | |
require 'erb' | |
require 'pp' | |
require 'fileutils' | |
include FileUtils::Verbose | |
Dir.chdir( File.dirname( __FILE__ ) ) | |
module SCM | |
%w( svn hg git ).each { |scm| | |
define_method( "scm_#{scm}?".to_sym ) { | |
File.exist?( ".#{scm}" ) and which( scm ) | |
} | |
} | |
end | |
include SCM | |
class Transfer | |
class InvalidConfiguration < StandardError; end | |
def initialize( conf = nil ) | |
if ( conf.has_key?( 'bin' ) and conf.has_key?( 'uri' ) ) | |
begin | |
@conf = conf | |
@bin = conf['bin'] | |
@uri = URI( conf['uri'] ) | |
rescue => e | |
bad_configuration( e, conf ) | |
end | |
else | |
bad_configuration( InvalidConfiguration.new, conf ) | |
end | |
end | |
attr_reader :bin, :uri | |
def bad_configuration( e, conf ) | |
puts "\n" + conf.to_yaml + "\n" | |
raise e | |
end | |
def target | |
case @bin | |
when 'lftp' | |
@uri.path | |
when 'rsync' | |
@uri | |
end | |
end | |
def opts( c ) | |
case @bin | |
when 'lftp' | |
"-u #{c.user},#{c.password} -e 'set ftp:list-options -a; mirror --delete -v <%= reverse %>-X #{ignores.join( ' -X ' )} <%= from %> <%= to %>; bye' #{c.host}" | |
when 'rsync' | |
"-rvt --exclude=#{ignores.join( ' --exclude=' )} --delete <%= from %> <%= to%>" | |
end | |
end | |
def up | |
prepare_htaccess( @conf ) | |
reverse = '-R ' | |
from = '.' | |
to = target | |
opts = ERB.new( opts( @uri ) ).result( binding ) | |
system( "#{@bin} #{opts}" ) | |
remove_htaccess | |
end | |
def down | |
reverse = '' | |
from = target | |
to = '.' | |
opts = ERB.new( opts( @uri ) ).result( binding ) | |
system( "#{@bin} #{opts}" ) | |
end | |
def self.minimum | |
{'production' => | |
{ 'bin' => 'lftp', | |
'uri' => 'ftp://user:pass@host/path'}, | |
'default' => 'production' | |
}.to_yaml | |
end | |
end | |
class BasicAuth | |
def self.minimum | |
return <<EOD | |
AuthType Basic | |
AuthName The restriction area for <%= host %> | |
AuthUserFile <%%= conf['htpasswd'] %> | |
Require valid-user | |
EOD | |
end | |
end | |
HTACCESS = '.htaccess' | |
HTACCESS_SEED = '_htaccess.txt' | |
SITEMAP = '_sitemap.html' | |
def rake_self | |
File.basename( __FILE__ ) | |
end | |
def win? | |
return ( RUBY_PLATFORM =~ /mingw/ or | |
(RUBY_PLATFORM =~ /win/ and | |
RUBY_PLATFORM !~ /darwin/ and RUBY_PLATFORM !~ /cygwin/ ) ) | |
end | |
def linebreak | |
if ( !@linebreak ) | |
@linebreak = ( win? ) ? "\r\n" : "\n" | |
end | |
return @linebreak | |
end | |
def which( cmd ) | |
path = ENV['PATH'] | |
path += File::PATH_SEPARATOR + '/Program Files' if win? | |
cmd += '.exe' if win? | |
paths = path.split( File::PATH_SEPARATOR ).select { |path| | |
File.exist?( path ) and File.exist?( File.join( path, cmd ) ) | |
} | |
if ( paths.size > 0 ) | |
paths.first + File::SEPARATOR + cmd | |
else | |
nil | |
end | |
end | |
def cut( cmd, chr = '?' ) | |
`#{cmd}`.map { |line| | |
c = line.split( /\s+/ ) | |
if ( c[0] =~ Regexp.new( "[#{chr}]" ) ) | |
c[c.size-1] | |
else | |
nil | |
end | |
}.compact | |
end | |
def htmlfiles | |
Dir.glob( "**/*.html\0**/*.htm" ) | |
end | |
def trans_conf | |
'_trans.yml' | |
end | |
def load_trans_conf( env = nil ) | |
if ( !@conf and File.exist?( trans_conf ) ) | |
@conf = YAML.load_file( trans_conf ) | |
end | |
return ( @conf and env and @conf.has_key?( env ) ) ? @conf[env] : @conf | |
end | |
def trans_envs | |
conf = load_trans_conf | |
env = conf.keys if ( conf.is_a?( Hash ) ) | |
( env ) ? env - ['default'] : [] | |
end | |
def sitemap_yaml | |
yaml = nil | |
%w( yaml yml ).each { |suffix| | |
%w( .sitemap _sitemap sitemap ).each { |basename| | |
file = "#{basename}.#{suffix}" | |
if ( File.exist?( file ) ) | |
yaml = file | |
break | |
end | |
} | |
} | |
return yaml | |
end | |
def load_sitemap | |
( sitemap_yaml ) ? YAML.load_file( sitemap_yaml ) : nil | |
end | |
def ignores | |
%w(*~ *.bak .*~ .*.bak .*ignore CVS/ .svn/ .hg/ .git/ .DS_Store Thumbs.db) + [rake_self, trans_conf, HTACCESS_SEED, SITEMAP, sitemap_yaml] | |
end | |
def prepare_htaccess( conf = nil ) | |
if ( File.exist?( HTACCESS_SEED ) ) | |
cp( HTACCESS_SEED, HTACCESS ) | |
open( HTACCESS, 'r+b' ) { |f| | |
erb = ERB.new( f.read ) | |
f.truncate( 0 ) | |
f.rewind | |
f.puts erb.result( binding ) | |
} | |
end | |
end | |
def remove_htaccess | |
if ( File.exist?( HTACCESS_SEED ) ) | |
rm( HTACCESS ) | |
end | |
end | |
LINEBREAK = linebreak | |
HTMLFILES = htmlfiles | |
TIDY = 'tidy -qe' | |
LINKCHECKER = 'linkchecker --stdin --no-status -a --ignore-url=^mailto:' | |
# | |
namespace :transfer do | |
trans_envs.each { |env| | |
namespace env do | |
trans = Transfer.new( load_trans_conf( env ) ) | |
if ( which( trans.bin ) ) | |
desc "upload contents from cwd to #{trans.uri.host}" | |
task :up => :clean do | |
trans.up | |
end | |
desc "download contents into cwd from #{trans.uri.host}" | |
task :down do | |
trans.down | |
end | |
end | |
end | |
} | |
c = load_trans_conf | |
if ( c ) | |
if ( c.has_key?( 'default' ) ) | |
trans = Transfer.new( c[c['default']] ) | |
if ( which( trans.bin ) ) | |
desc "upload contents from cwd to #{trans.uri.host}" | |
task :up => :clean do | |
trans.up | |
end | |
desc "download contents into cwd from #{trans.uri.host}" | |
task :down do | |
trans.down | |
end | |
end | |
end | |
namespace :conf do | |
desc "show transfer config" | |
task :get do | |
puts c.to_yaml | |
end | |
end | |
end | |
namespace :conf do | |
desc "set transfer config" | |
task :set do | |
if ( !File.exist?( trans_conf ) ) | |
puts "Hit any key and edit transfer setting with your favorite EDITOR" | |
ARGV.replace( [] ) | |
gets | |
File.open( trans_conf, 'wb' ) { |f| | |
f.puts( Transfer.minimum ) | |
} | |
end | |
system( ENV['EDITOR'], trans_conf ) | |
end | |
end | |
end | |
if ( which( 'tidy' ) and HTMLFILES.size > 0 ) | |
desc 'check syntax of htmls' | |
task :tidy do | |
check = HTMLFILES.map { |html| | |
Open3.popen3( TIDY + " #{html}" ) { |stdin, stdout, stder| | |
e = stder.read | |
html + LINEBREAK + e if e.size > 0 | |
} | |
}.compact.join | |
if ( check.size > 0 ) | |
puts check | |
else | |
puts "All html file's syntax OK." | |
end | |
end | |
end | |
if ( which( 'linkchecker' ) and HTMLFILES.size > 0 ) | |
desc "check links in htmls" | |
task :linkcheck do | |
Open3.popen3( LINKCHECKER ) { |stdin, stdout, stderr| | |
stdin.puts HTMLFILES.join( ' ' ) | |
stdin.close | |
started = false | |
result = {} | |
check = stdout.read.split( LINEBREAK ).map { |line| | |
if ( !started ) | |
if ( line =~ /\AStart checking/ ) | |
started = true | |
end | |
nil | |
else | |
if ( line =~ /\AThat's it/ ) | |
result['warning'] = $1.to_i if ( line =~ /([0-9]+) warnings/ ) | |
result['error'] = $1.to_i if ( line =~ /([0-9]+) error/ ) | |
end | |
line | |
end | |
}.compact.join( LINEBREAK ) | |
if ( (result.has_key?( 'warning' ) and result['warning'] > 0) or | |
(result.has_key?( 'error' ) and result['error'] > 0) ) | |
puts check | |
else | |
puts "All html file's link OK." | |
end | |
} | |
end | |
end | |
if ( sitemap_yaml ) | |
desc "create sitemap html from #{sitemap_yaml}" | |
task :sitemap_html do | |
require 'xoxo' | |
open( SITEMAP, 'w' ) { |f| | |
f.puts load_sitemap.to_xoxo | |
} | |
end | |
end | |
if ( methods.grep( /\Ascm_/ ).size > 0 ) | |
desc "list untracked files" | |
task :untracked do | |
if ( scm_hg? ) | |
puts cut( 'hg stat -A', 'I?' ) | |
elsif ( scm_git? ) | |
puts `git ls-files -o` | |
elsif ( scm_svn? ) | |
puts cut( 'svn stat -v', '?' ) | |
end | |
end | |
end | |
if ( methods.grep( /\Ascm_/ ).size > 0 and | |
!( scm_git? and methods.grep( /\Ascm_/ ).size == 1 ) ) | |
desc "list missing files" | |
task :missing do | |
if ( scm_hg? ) | |
puts cut( 'hg stat -A', '!' ) | |
elsif ( scm_svn? ) | |
puts cut( 'svn stat -v', '!' ) | |
else | |
puts "noncompliant scm tool only" | |
end | |
end | |
end | |
if ( methods.grep( /\Ascm_/ ).size > 0 ) | |
desc "diffs of all tracking files" | |
task :diff do | |
if ( STDOUT.tty? ) | |
color = which( 'colordiff' ) ? ' | colordiff' : '' | |
pager = ENV['PAGER'] ? " | #{ENV['PAGER']}" : '' | |
end | |
if ( scm_hg? ) | |
system( "hg diff#{color}#{pager}" ) | |
elsif ( scm_git? ) | |
system( "git diff#{color}#{pager}" ) | |
elsif ( scm_svn? ) | |
system( "svn diff#{color}#{pager}" ) | |
end | |
end | |
end | |
if ( HTMLFILES.size > 0 ) | |
desc "s/{WAVEDASH}/{FULLWIDTH_TILDE}/ (utf-8)" | |
task :fix_wavedash4oldwin do | |
system( <<EOD ) | |
find . -name '*.html' -o -name '*.htm' -o -name '*.css' -o -name '*.js' | xargs grep -l 〜 | xargs perl -i.bak -pe 's/〜/~/' | |
EOD | |
end | |
end | |
if ( !File.exist?( HTACCESS_SEED ) and | |
c = load_trans_conf( 'production' ) and | |
trans = Transfer.new( c ) ) | |
desc 'create BASIC-Auth setting' | |
task :create_basic_auth do | |
open( HTACCESS_SEED, 'wb' ) { |f| | |
host = trans.uri.host | |
f.puts( ERB.new( BasicAuth.minimum ).result( binding ) ) | |
} | |
touch( '.htpasswd' ) | |
# same as ftp setting | |
system( "htpasswd -b .htpasswd #{trans.uri.host} #{trans.uri.password} > /dev/null 2>&1" ) | |
end | |
end | |
desc "test html files" | |
task :test => [:tidy, :linkcheck] do; end | |
desc "remove backup and auto creation file" | |
task :clean do | |
Dir.glob( "**/*~\0**/.*~\0**/.DS_Store\0**/Thumbs.db\0**/._*" ).map { |e| | |
rm( e ) | |
} | |
end | |
task :default do | |
if ( HTMLFILES.size == 0 and !trans_conf and !sitemap_yaml ) | |
puts 'No HTML file, No Task.' | |
else | |
app = Rake.application | |
app.options.show_task_pattern = Regexp.new('') | |
app.display_tasks_and_comments | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment