git for hostmonster

2010-05-02 14:17:41 +0800

前段时间对网站做了些更新,于是在本地修改了代码,再git push,谁知却得到“bash: git-receive-pack: command not found”的error,我的git repository是放在hostmonster服务器上面的,之前都是正常的,于是提交ticket给hostmonster的support,得到的答复是他们升级的openssh,通过git+ssh不会再读取.bashrc或.ssh/environment文件,也就是说通过git+ssh没有办法修改PATH了。

没办法了,只能手动将命令的路径补全了,对于git pull/git ps来说,只需要在输入命令的时候增加参数,比如

git clone --upload-pack=/home1/huangzhi/git/bin/git-upload-pack
git push --receive-pack=/home1/huangzhi/git/bin/git-receive-pack

不过每次都输入参数实在麻烦,直接写到配置文件.git/config

[remote "origin"]
uploadpack=/home1/huangzhi/git/bin/git-upload-pack
receivepack=/home1/huangzhi/git/bin/git-receive-pack

然后就可以像以前一样git pull/git ps了。

还有一个问题,那就是capistrano。默认capistrano通过git  ls-remote获取最新的commit id,通过git clone来获取最新文件,但是这些命令都没有办法设置upload-pack和receive-pack参数,没办法,只能修改默认的方法定义。

首先是git ls-remote

require 'capistrano/recipes/deploy/scm/base'
::Capistrano::Deploy::SCM::Base.class_eval do
  alias_method :origin_scm, :scm
  def scm(*args)
    if command == "git" and args[0] == "ls-remote"
      args[0] = "ls-remote --upload-pack=/home1/huangzhi/git/bin/git-upload-pack"
    end
    origin_scm(args)
  end
end

当命令为git ls-remote的时候,额外加入参数upload-pack

再就是git checkout

require 'capistrano/recipes/deploy/scm/git'
::Capistrano::Deploy::SCM::Git.class_eval do
  def checkout(revision, destination)
    git    = "/home1/huangzhi/git/bin/git"
    remote = origin

    args = []
    args << "-o #{remote}" unless remote == 'origin'
    if depth = configuration[:git_shallow_clone]
      args << "--depth #{depth}"
    end

    execute = []
    if args.empty?
      execute << "#{git} clone --upload-pack=/home1/huangzhi/git/bin/git-upload-pack #{verbose} #{configuration[:repository]} #{destination}"
    else
      execute << "#{git} clone --upload-pack=/home1/huangzhi/git/bin/git-upload-pack #{verbose} #{args.join(' ')} #{configuration[:repository]} #{destination}"
    end

    # checkout into a local branch rather than a detached HEAD
    execute << "cd #{destination} && #{git} checkout #{verbose} -b deploy #{revision}"
    
    if configuration[:git_enable_submodules]
      execute << "#{git} submodule #{verbose} init"
      execute << "#{git} submodule #{verbose} sync"
      execute << "#{git} submodule #{verbose} update"
    end

    execute.join(" && ")
  end
end

这个我没有找个比较优雅的方式,只能直接覆盖原来的方法定义,并在git clone的命令中加入upload-pack。

还有就是当capistrano执行远程命令的时候,同样没有合适的environments,比如执行rake db:migrate的时候,所以需要修改默认的rake命令

set :rake, "source /home1/huangzhi/.bashrc; rake"

这样当执行rake命令执行,首先读取.bashrc,设置合适的environments,然后再执行rake命令。

到此为止,一切又恢复了正常。

Hostmonster升级到Rails2.3.3

2009-07-25 22:11:15 +0800

前两天Hostmonster把Rails升级到2.3.3,导致我的网站无法访问。查看日志,Dispatcher failed to catch: undefined method `read' for class `FCGI::Stream' (NameError),给Hostmonster提交了ticket,到现在都还没有结果,没办法,只能靠自己了。

google了一下,可能是rack中的一段代码的问题。

首先,安装好自己的gem repository,并且在environment.rb中指定:

ENV['GEM_PATH'] = '/home7/huangzhi/ruby/gems:/usr/lib/ruby/gems/1.8'

然后,指定app的rails为2.3.3:

RAILS_GEM_VERSION = '2.3.3' unless defined? RAILS_GEM_VERSION

最后,修改gem中rack-1.0.0/lib/rack/handler/fastcgi.rb文件,将第7行注释掉

#  alias _rack_read_without_buffer read

OK,这样就可以顺利访问啦!

在hostmonster上搭建git server

2009-06-14 21:21:10 +0800

hostmonster本身并不支持git,不过还好它提供了ssh,我们可以ssh上去编译git。

首先,ssh到hostmonster上,编译安装git

$ mkdir git
$ cd git
$ wget http://kernel.org/pub/software/scm/git/git-1.6.3.2.tar.gz
$ tar -zxvf git-1.6.3.2.tar.gz
$ cd git-1.6.3.2/
$ ./configure --prefix=$HOME/git
$ make && make install

修改~/.bashrc,设置环境变量

export GIT_HOME=$HOME/git
export PATH=$GIT_HOME/bin/:$GIT_HOME/lib/libexec/git-core/:$PATH

验证结果

$ source ~/.bashrc
$ git --version

我们在本地新建一个rails app来使用git server

$ rails home -d mysql
$ cd home
$ git init
$ git add .
$ git commit -a  -m "first commit"

忘了说了,你应该git init之后新建一个.gitignore文件

.DS_Store
log/*.log
tmp/**/*
config/database.yml

在本地生成一个只包含版本信息的版本库,并上传到hostmonster上,假设你要把你hostmonster上的版本库信息放在gits目录下

$ cd ../
$ git clone --bare home home.git
$ touch home.git/git-daemon-export-ok
$ scp -r home.git username@yourdomain.com:gits/

设置本地代码的远程版本库

$ cd home
$ git remote add origin usernmae@yourdomain.com:gits/home.git

之后就是修改代码,git pull/git push了

贴贴我通过Capistrano自动发布到Hostmonster的deploy.rb文件吧

set :application, "huangzhimin.com"
set :repository,  "GIT_REPOSITORY"
set :user, "huangzhi"
set :scm, :git
set :deploy_to, "DEPLOY_DIRECTORY"

role :app, "www.huangzhimin.com"
role :web, "www.huangzhimin.com"
role :db,  "www.huangzhimin.com", :primary => true

set :use_sudo, false
set :run_method, :run

namespace(:deploy) do
  task :after_update_code, :roles => :app do
    run "ln -s #{shared_path}/config/database.yml #{current_release}/config/database.yml"
    run "cp #{shared_path}/config/environment.rb #{current_release}/config/"
    run "chmod -R u+rwX,go-w #{current_release}/public #{current_release}/log"
  end

  task :restart do
    web.disable
    migrate
    cleanup
    web.enable
  end
end

namespace :web do
  desc "Serve up a custom maintenance page."
  task :disable, :roles => :web do
    require 'erb'
    on_rollback {run "rm #{shared_path}/system/maintenance.html"}
    reason = ENV['REASON']
    deadline = ENV['UNTIL']
    template = File.read("#{current_release}/app/views/maintenance/index.html.erb")
    page = ERB.new(template).result(binding)
    put page, "#{shared_path}/system/maintenance.html", :mode => 0644
  end
end

将GIT_REPOSITORY和DEPLOY_DIRECTORY分别替换为你自己的git repository地址,和rails app发布到的目录。我是自己在Hostmonster上面建了一个git server,然后设置好公私密钥,这样省去了每次都输入密码的烦恼。

第四行将scm设为git,默认是svn的,所以要显示设置一下。

15-19行,定义在部署之后要做的操作:
1. 为config/database.yml做一个软链接,因为database.yml文件是被git ignore的。
2. 复制config/environment.rb文件,因为需要加入ENV['RAILS_ENV'] ||= 'production'这句话,强制使用production环境。
3. 设置public和log的目录权限为755,因为在hostmonster上面public的目录对其它用户必须是不可写的,而capistrano默认是设置为775的。

31-39行,则是在部署的时候生成一个maintenance.html文件,表示系统正在维护,等部署完毕之后再删除,提供给用户一个比较友好的错误页面。

本来restart的task是用./script/process/reaper --action=restart --dispatcher=dispatch.fcgi命令的,不过rails2.3.2中已经没有process目录了,只得作罢。