Skip to content

Instantly share code, notes, and snippets.

@wtnabe
Created October 19, 2009 22:50
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 wtnabe/213791 to your computer and use it in GitHub Desktop.
Save wtnabe/213791 to your computer and use it in GitHub Desktop.
A Rakefile for simple web site creation and maintenance
# -*- 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