[哈菜系列二] Git@Oschina + 本地 Homestead + 阿里云 Server 代码半自动部署

好吧。。我又来了。。。

连续奋战了10+个小时之后,才搞定的,请允许我来显摆下- -

痛点:

菜鸟爱折腾,正好最近在看微信相关的开发,需要频繁发布到阿里云上做调试,(再加上系统部署成这样了,懒得重新搞),于是就估摸着如何按照自己的环境搞一个半自动发布。(就是自己这边push之后,对端也部署完成的环境)。

翻阅论坛时,发现大神们已经对自动部署有了自己的方式,@summerblue和@lijinma都有在这里讨论,在此感谢@lijinma的指导^^。

应用环境:

本地开发:Mac + Homestead + Laravel 5

Git仓库:OSChina

远端服务器:阿里云 CentOS + Apache

涉及知识:

  • Laravel 5 :
    Console Command,Route,Middleware

  • OSChina:
    Hook

  • CentOS:
    SSH

遇到的坑:

  1. Laravel 5 Csrf自动拦截入站请求。解决方法:自己写一个Middleware把Csrf验证替换下。
  2. Json知识薄弱,在读取OSChina传过来的值的时候纠结了很久。
  3. Apache权限问题。解决方法:参考老外的方式,用户组赋权。传送门

You should think carefully about the security implications of either choice, but the second option is probably easiest. If you don't already have such a group, you can create it with:


addgroup gitwriters

... and then add yourself and the Apache user to this group:


adduser [yourusername] gitwriters
adduser apache gitwriters

Then you can follow the instructions in another question to change the permissions on the repository. To reiterate those with some slight variations:

Recursively, set the group ownership of every file and directory of your repository:


chgrp -R gitwriters /path/to/your/repo

Recursively, make every file and directory of your repository readable and writable
by the group:


chmod -R g+rw /path/to/your/repo

Recursively, set the setgid of every directory in the repository. The setgid bit
on directories means that files created in the directory will have the same group
ownership as the directory.


find /path/to/your/repo -type d -print0 | xargs -0 chmod g+s

Then hopefully your git pull should work.

  1. SSH权限问题。最最坑爹的地方,反复调试了很久,结果是在OSChina没有设置对应的Apache ssh key导致的。对于apache这种nologin的用户,请使用sudo -u apache ssh-keygen -t rsa "xxx@xxx.com"的形式生成rsa文件。

源代码及设置:

请打开OSChina的相关项目的管理->PUSH钩子。

并设置相应的网址和密码,这里用了http://www.abc.com/git 密码为password。

app/Http/routes.php

//for OSChina git
Route::post('/git', 'Misc\GitController@git');

app/Http/Misc/GitController.php


<?php namespace app\Http\Controllers\Misc;

use Illuminate\Http\Request;
use app\Http\Controllers\Controller;

class GitController extends Controller {

    function git(Request $request)
    {
        $r = json_decode($request->get('hook'));
        $exitCode = \Artisan::call('git', ['password' => $r->password]);

    }

}

app/Console/Commands/Git.php


<?php namespace app\Console\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;

class Git extends Command {

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'git';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'git pull for OSCHINA Post.';

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function fire()
    {
        //
        if ($this->argument('password') == 'password')
        {
            $result = shell_exec('cd /mnt/www/app && git pull');

            \Log::info('Git Push Result: ' . $result);

        }
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return [
            ['password', InputArgument::REQUIRED, 'An example argument.'],
        ];
    }

}

app/Http/Middleware/CheckInBound.php


<?php namespace app\Http\Middleware;

use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

/**
 * Class CheckInBound
 * used to determine the inbound data flow
 *
 * by yyf
 *
 * @package scms\Http\Middleware
 */
class CheckInBound extends BaseVerifier {

    private $safePath=[
        'wechat', //微信
        'git' //OSChina Git push
    ];

    private $safeHead=[];

    /**
     * Handle an incoming request and do the VerifyCsrfToken jobs.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
         $type=$request->path();
        \Log::info($request->all()); // log the request
        if(in_array($type,$this->safePath)){
            return parent::addCookieToResponse($request, $next($request));
        }
        return parent::handle($request, $next);

    }

}

app/Http/Kernel.php


...
//'app\Http\Middleware\VerifyCsrfToken', //use CheckInBound instead by yyf
        'app\Http\Middleware\CheckInBound',
...

历史遗留问题

感谢各位大侠看到这里,大侠请留步,小弟还有一事不懂,还请指教。

俗话说没有Bug的程序不是好程序...

目前Middleware在调用完Artisan Command之后,会返回错误:


production.ERROR: exception 'ErrorException' with message 'Argument 1 passed to Illuminate\Session\Middleware\StartSession::addCookieToResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given, called in /mnt/www/xxx/vendor/compiled.php on line 10877

一般这种是return个OK了事呢,还是又更好的写法呢?求大师们指点。^^

已修复~ 参见@lijinma大神回复。。

本帖已被设为精华帖!
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 4
hareluya

话说,最近Slack是不是也挂了。。。今天想请教,结果连不上的说-0-

9年前 评论
hareluya

@lijinma 果然有效... 感觉是对request在middleware中的流程不是很清楚导致的。的确是应该要有return response才行。感谢大神!

9年前 评论
hareluya

@nidesky 777的权限安全问题还是需要考虑下的哟~
建议用用户组的形式解决。。

9年前 评论

你好,怎么解决的。我也遇到这个问题了。

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!