Help us understand the problem. What is going on with this article?

Gulp+Sftp+BrowserSyncでリモートサーバへのPush+リロードを自動化させる

More than 3 years have passed since last update.

はじめに

表題の通りですが、前々からこういったことがやりたいと思っていました。今回Gulpを使うことで簡単に実現できた気がするので、メモとしてまとめました。何かのお役にでも立てば幸いです。

[目次]

  • やりたいこと
  • メリット
  • デメリット
  • 用意するもの
  • ファイル構成
  • 環境構築の流れ
  • 実行してみる
  • 終わりに

やりたいこと

  1. ローカルでファイルを修正
  2. 同時にリモートサーバへ修正したファイルをアップロード
  3. そのまま該当サイトを表示したブラウザを自動リロード

メリット

  • ローカルにリモートと同じ環境を再現する必要がない
  • リモートサーバ側で何か設定する必要がない
  • 好きなエディタでファイルの修正ができる(エディタ独自の機能を使わない)
  • SFTPでファイル送信しているだけなので、いつどんなタイミングでも始める・止めることができる
  • Gulpなのでプリプロセッサやテンプレートと自由に組み合わせることができる
  • BrowserSyncを使っているので複数ブラウザでもOK

デメリット

  • gulpの導入が必要
  • 同名ファイルは上書きされるのでやや破壊的な操作になる(特にPATHの指定には注意)

用意するもの

  • gulpが実行できる環境
  • パッケージ: gulp-sftp
  • パッケージ: browser-sync
  • リモートサーバの接続情報をまとめたjsonファイル
  • その他やりたいことに応じたパッケージなど
  • ftp接続の場合は、gulp-ftpとかでも良いかもしれません
  • また動作は以下のVersionで確認しました(2016-03-08現在
名前 Version
node.js 4.2.1
gulp 3.9.1
gulp-sftp 0.1.5
browser-sync 2.11.1

ファイル構成

┬─ .sftpconfig.json
├─ gulpfile.js
├─ html/ // アップするファイルのドキュメントルート
├─ package.json
└─ node_modules/

環境構築の流れ

パッケージのインストール

まずはGulpとパッケージをインストールします

$ cd ../作業ディレクトリ/
$ npm init
$ npm install --save-dev gulp
$ npm install --save-dev gulp-sftp
$ npm install --save-dev browser-sync

必要なファイルの作成

タスクを定義するgulpfile.jsとリモートサーバの接続情報を記載した.sftpconfig.jsonを作成します。jsonのファイル名や格納場所は何でも良いと思います(gulpfile内で指定しているため※後述)。当然ですが素のID/PWを記載する場合は扱いに注意しましょう。

sftpconfig.json
{
  "host": "192.168.33.40",
  "port": 22,
  "user": "vagrant",
  "pass": "vagrant",
  "remotePath": "/var/www/html/public_html/php/test/"
}

その他オプションや秘密鍵の設定などはgulp-sftpのドキュメントを確認ください

ここではリモートサーバ=vagrantとして動作を試しました。なおvagrantであればsync_folder機能が使えるので本来Sftpは不要ですが...

gulpfile.jsにタスクを定義

ここでは主に以下のタスクを定義します。

  • gulp default: Proxy Serverのbuildとファイルの監視→Sftp+リロード
  • gulp sftp:all: 指定ディレクトリ以下をまとめてSftpアップロード
gulpfile.js
var gulp = require('gulp'),
    fs   = require('fs'),   // node.js api
    path = require('path'), // node.js api
    sftp = require('gulp-sftp'),
    bs   = require('browser-sync').create();

/**
 * ファイルを読み込むための関数
 * @param  {string} 読み込みたいファイル(パス指定
 * @return {object} ファイルの中身を返す
 */
var loadJsonSync = function(filename) {
  return JSON.parse(fs.readFileSync(filename, 'utf8'));
};

/* 基本設定 */
var base = {
      srcDir: 'src/',  // 今回のサンプルでは不使用
      distDir: 'html/'
    },
    conf = {
      // SFTPを発動させる対象
      watchPath: [
        base.distDir + '**/*.html',
        base.distDir + '**/*.php',
        base.distDir + '**/*.css',
        base.distDir + '**/*.js'
        // 対象から外す記述サンプル
        // '!' + base.distDir + '**/*.html'
      ],
      sftp: {
        // サーバ情報を記載したjsonファイルのパスを指定
        server :'./.sftpconfig.json',
        // sftp:allタスクを使用するときにアップする対象
        localPath: [
          base.distDir + '**'
        ],
      },
      browserSync: {
        proxy: {
          proxy: '192.168.33.40',
          port: 9999,
          open: false,
          notify: false
        }
      }
    };

/**
 * 変更ファイルのアップ+リロードタスクを実行する関数
 * @param {string} localPath アップロードするファイル
 * @param {string} remotePath アップロード先(ティレクトリ)
 */
var uploadAndReload = function(localPath, remotePath) {
  var settingFile = loadJsonSync(conf.sftp.server);
  if (remotePath) {
    settingFile.remotePath += remotePath;
  }
  gulp.task('sftp', function() {
    console.log('ファイルアップします');
    return gulp.src(localPath).pipe(sftp(settingFile));
  });
  // sftpタスクが完了してからreloadを実行する
  gulp.task('reload', ['sftp'], function() {
    console.log('リロードします');
    bs.reload();
  });
  gulp.start(['reload']);
};

/************************************************
 * タスク一覧
 ************************************************/
/* browserSyncのビルド */
gulp.task('server', function() {
  bs.init(conf.browserSync.proxy);
});

/* 監視タスク */
gulp.task('watch', function() {
  gulp.watch(conf.watchPath, function(e) {
    // 変更ファイルのpathを取得
    // c:/Users/...始まりだが動作に影響はないためこのまま
    var localPath  = e.path;

    // アップロード先のpathを取得
    // sftpconfig.jsonのremotePathの後ろに追加する前提
    // そのためベースディレクトリ以下からファイルまでのディレクトリ名を取得(ベースディレクトリを除く)
    // バックスラッシュが入ってきた場合に備えて / に置換
    var remotePath = path.relative(base.distDir, path.dirname(e.path)).replace(/\\/g, '/') + '/';

    uploadAndReload(localPath, remotePath);
  });
});

/* 全アップロード */
gulp.task('sftp:all', function() {
  var settingFile = loadJsonSync(conf.sftp.server);
  gulp.src(conf.sftp.localPath).pipe(sftp(settingFile));
});

/* defaultタスク */
gulp.task('default', ['server'], function() {
  gulp.start(['watch']);
});

注意事項

  • jsonのremotePathとbase.distDirが上手く結合できない場合は調整します
  • 複数ファイルの同時変更時はリロードのタイミングがズレるかもしれません
  • リモート側に同じディレクトリがないとアップできません(windowsのみ?
  • その場合リモート側に同じディレクトリを用意するかsftp:allで一旦丸ごとアップします

実行してみる

gulpを実行して動作を確認します。
ライブリロードを動作させるにはlocalhost:portをブラウザで開く必要があります。
proxyを指定した192.168.33.40の方ではありません!(proxyのオプションopen: trueとすれば自動で開きます)

$ gulp
# ファイルを修正

[コンソールイメージ]
CLI_gulp.gif

[画面イメージ]
sftp-reload.gif

SFTPとリロードが上手くいきました!

終わりに

ライブリロードは非常にストレスフリーなので、あらゆる環境で導入したいと思っていました。またプロジェクトによって変更すべき箇所は少ないので、非プログラマーの方でも気軽に使えるかもしれません。なお同名のファイルは上書きしてしまうのでパスの指定にはご注意を!

何かおかしな所やこうした方が良いんじゃ?ということがあればご指摘ください!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away