こんにちは、デザイナーの岩淵です。
技術系記事のためちょっと長くなります。

LYZONではGitホスティングサービスの中でもBitbucketを利用するシーンが多いです。
デザイナーの業務としてはHTML/CSS/JS周りを作成し表示確認用サイト(レンタルサーバー)へアップロードすることがあるのですが、Bitbucketでソースコード管理している場合はBitbucket Pipelinesを利用すれば面倒な設定を極力行わず簡単にCI/CD(継続的インテグレーション/継続的デリバリー)を導入し、一部の作業を自動化することができます。

この記事ではBitbucket Pipelinesを利用して、Bitbucketリポジトリの特定のブランチへプッシュした際にビルド&レンタルサーバーへFTPデプロイするという処理を作っていきたいと思います。

目次

    Bitbucket Pipelinesとは

    Atlassian社が提供するCI/CDツールです。
    Bitbucketと連動したサービスなので、Bitbucketユーザーは他サービスをCI/CDとして利用する場合の連携手順が不要ですぐに利用することが可能です。

    ドキュメントも公式サイトに充実しているので扱いやすいと思います。

    料金体系

    プランによって色々とオプションが違うようですが、クラウド型の場合基本的にはビルド時間に対して課金される従量課金タイプです。

    Bitbucket無料ユーザーなら月50分まで無料で利用でき、スタンダードなら月2500分プレミアムなら月3500分まで課金なしで使えます。
    月のビルド時間が超過するとそれ以降はビルド時間1000分で10ドルかかるようです。
    ※2020年12月時点の情報です。

    約200分を使用する小さなチームが見られますが、5~10人の開発者のチームは通常パイプラインで月に400~600分を使用します。

    公式サイトでこのような目安があります。50分はテストしているうちに軽く越してしまうので、複数人で扱う場合は無料プランだと追加料金が発生する可能性が高いですね。

    この記事で目指す動作

    この記事では主にデザイナー・コーダー・フロントエンドエンジニア向けとして、yarnコマンドでビルドしたファイルをレンタルサーバーへFTPデプロイするまでの動作を自動化することを目指していきます。

    シンプルに以下のようなイメージです。

    1. developブランチへプッシュ
    2. 【Pipelines】yarn installやらyarn devなどのコマンドでビルド
    3. 【Pipelines】ビルド後の指定ファイルをレンタルサーバーへFTPデプロイ

    ビルドやデプロイを自動化することで得られる恩恵は大きいですね。
    ビルド後のファイルをGit管理しなくてもいいのでコンフリクトの可能性を減らせたり、違うブランチをサーバーへ反映してしまうなど人為的ミスも減ります。

    【実践】リポジトリの初期設定方法

    ※以下手順は2020年12月時点の方法です。操作画面など変更されている可能性があります。

    まずはBitbucket Pipelinesを導入したいリポジトリの管理画面へ移動し、サイドバーからRepository settingsを選択します。

    少しスクロールするとPIPELINESの項目たちが見えてきます。
    ここのSettingsに入ります。


    するとEnable Pipelinesのチェックがあるので押して有効にします。
    このチェックだけで「このリポジトリでPipelinesを利用する」という設定が終了しました。

    チェックを入れるとすぐ下のConfigure bitbucket-pipelines.ymlがアクティブになるので、これを押して次の画面に移ります。(このボタンはただのページ遷移なので押さなくても設定は終わっています)


    翻訳「やったー!これでパイプラインが正常に設定されました。パイプラインが起動すると、ここに詳細が表示されます。」
    という画面に移りました。これであとはymlさえあればPipelinesが動いてくれるようになります。

    【実践】ビルド&デプロイする方法

    リポジトリ管理画面左側のサイドバーからDeploymentsを選択します。

    スクロールするとこのようなエリアが見えてきます。ここでbitbucket-pipelines.ymlを作成することができます。
    このファイルがリモートに存在していることで、プッシュされた際にymlに記載されている設定をもとにスクリプトを動かすことが可能です。
    この画面から作成しなくてもテキストエディタなどでbitbucket-pipelines.ymlを作成しプッシュするという方法でも大丈夫です。

    右側のChange the templateの中には、よく使われる環境のテンプレートを選ぶことができます。
    今回はNode.jsでのビルドを行うので、テンプレートの中からBuild and test a NodeJS codeを選択してください。


    他のテンプレートに比べて内容が少ないので分かりやすいと思います。赤文字はコメントで、その下からが処理内容です。
    ざっくり説明すると以下のような処理になっています。

    1. image: node:10.15.3
      はじめに使用するimageを選んでいます。ここのnodeのバージョンはプロジェクトに合わせて変更しないとスクリプトが動かないかもしれません。
    2. pipelines
      Pipelinesの処理ですよという部分です。
    3. default
      このテンプレートにはありませんが、「branches」というところで例えばdevelopを指定するとdevelopブランチにプッシュされたときの挙動を指定できます。
      そのようにbranchesで指定されているブランチ以外のブランチすべてで動作することになるのが「default」です。
    4. parallel
      下にある2つの「step」を同時に実行することを意味します。「parallel」がなければ「step」は順次実行になります。
    5. step
      ステップです。ビルド単位ごとに分けたりするためにあります。
    6. name
      ここでは「step」の名前です。処理には関係していません。処理内容を見る画面で何が行われているのか分かりやすくなります。
    7. caches
      キャッシュのことで、次回Pipelinesを動かした際にnpm installなどの処理を短縮することができます。ここではnodeと書かれていますが、これはnode_modulesのことです。
      注意点として、ここはトップレベルのnode_modulesを参照する記述なので、ビルド環境がフォルダに格納されていて、その中にnode_modulesが存在する場合は正確なパスで指定する必要があります。
    8. script
      コマンドで実行する内容になっています。npm installしたあとにnpm testするなどの内容がテンプレートにありますね。

    細かいymlの書き方は下記リンク先を参考にしてください。日本語ドキュメントで分かりやすいです。

    ビルド&デプロイするために、このようなymlを作成しました。

    
    image: node:10.22.0
    
    pipelines:
      branches:
        develop:
         - step:
            name: install & dev
            caches:
              - node-modules
              - common-images
              - yarn
            script:
              - cd Design
              - yarn install
              - yarn dev
            artifacts:
              - Design/source/**
              - Design/common/**
         - step:
            name: deploy
            deployment: Test
            script:
              - cd Design
              - pipe: atlassian/ftp-deploy:0.3.3
                variables:
                  USER: $FTP_USER
                  PASSWORD: $FTP_PASSWORD
                  SERVER: $FTP_SERVER
                  REMOTE_PATH: $FTP_DIRECTORY
                  LOCAL_PATH: '${BITBUCKET_CLONE_DIR}/Design/'
                  DEBUG: 'true'
                  EXTRA_ARGS: '--delete --exclude=work --exclude=node_modules --exclude=lib --exclude=.eslintrc.json --exclude=babel.config.js --exclude=gulpfile.js --exclude=modernizr-config.json --exclude=package.json --exclude=README.md --exclude=yarn.lock --exclude=webpack.config.base.js --exclude=webpack.config.development.js --exclude=webpack.config.production.js --exclude=webpack.config.staging.js --exclude-glob=*.bat --exclude=.htaccess --exclude=.htpasswd'
    
    definitions:
      caches:
        node-modules: Design/node_modules
        common-images: Design/common/images
        yarn: /usr/local/share/.cache/yarn
    

    以下の内容になります。

    前提

    1. nodeのバージョンは開発時に使用していた10.22.0を指定
    2. developブランチにプッシュされたときのみに動作(「branches」項目でdevelopブランチを指定)
    3. 以下の構成のリポジトリでPipelinesを動かす
      開発環境はDesignフォルダの中にある(Design/node_modules)
      
      Repository
      ├ .gitignore
      ├ bitbucket-pipelines.yml
      └ Design
        ├ node_modules
        ├ package.json
        ├ common ←デプロイ対象
        │ ├ fonts
        │ ├ images
        │ ├ js
        │ └ css
        ├ source ←デプロイ対象
        │ └ html
        ├ work ←ビルド対象
        │ ├ pug
        │ ├ sass
        │ ├ js
        │ ├ icons
        │ └ images
        └ その他コンフィグファイルなど
      

      workの中にあるファイルをコンパイルしてsourceとcommonに書き出す、というビルド内容になっています。

    step name: install & dev

    1. caches
      キャッシュさせたいフォルダなど記載していますが、これらは独自に定義した名称になっています。定義はコードの最下部にあるdefinitionsという部分で行っています。
    2. script
      まずはcd Designで開発環境へ移動します。その後にyarn installを行い、yarn devでビルドします。
    3. artifacts
      ステップを分けることによって何もしなければ次のステップでもクローンした状態からスタートします。
      ここではビルドステップのファイルをデプロイステップで使用したいため、このartifactsで引き継ぐファイルを指定しています。

    step name: deploy

    1. deployment: Test
      どの環境へのデプロイなのかを示します。Bitbucketのダッシュボードで各環境の状況を一覧することができます。デフォルトではTest・Staging・Productionの3つが定義されています。
    2. script
      1. cd Designで移動します。ステップをまたぐと位置が戻るので再度必要です。
      2. pipe: atlassian/ftp-deploy:0.3.3」これはAtlassianから提供されているFTPデプロイ用のタスクになります。

        USERやPASSWORDにレンタルサーバーのFTP情報を記載しています。「$FTP_USER」などとありますがこれはBitbucketのリポジトリ変数です。管理画面から変数を登録しここで呼び出しています。

        LOCAL_PATHに${BITBUCKET_CLONE_DIR}とあります。これはリポジトリの一番上から参照しているということです。cd Designで移動などすることがあるので、このように参照しておくと正確になります。

        EXTRA_ARGS」に大量の記載がありますね。ここはlftpコマンドに渡される追加の引数になります。「--exclude=」でFTP対象から除外するファイルを指定しています。「--exclude-glob」を使えばglobパターンで指定できます。(*.batなど)
        LOCAL_PATHは1つのフォルダまでしか指定できないようで、今回はDesign直下のフォルダ複数を対象としているため仕方なくここで多くのファイルを除外しています。
        「--delete」はミラーリングのためにあります。デプロイするファイルの中にないファイルがデプロイ先に存在していれば削除します。
        右までスクロールしていただければ分かるのですが「.htaccess」「.htpasswd」がありますね。レンタルサーバーの管理画面からBASIC認証をかけている場合などは、リポジトリにはこれらのファイルが含まれていません。つまり「--delete」を使うと削除されてしまいますのでここで除外をすることで削除を避けられます。

    definitions

    ユーザー定義を追加できます。今回はキャッシュに残したいフォルダを定義しています。
    yarnの部分は「node_modules単体をキャッシュしただけではインストール時に再度フルでインストールされてしまう」という現象を回避するためにあるおまじないです。yarnではなくnpmで動かしている場合は必要なさそうです。

    上記で作成したymlをコピーして入力エリアへ入れます。

    変数を定義したいので、右側からAdd variablesを選択してください。

    リポジトリ全体で使いたいので「Repository」の部分に入力していきます。
    見えてたらマズイ部分はSecuredをチェックして●●●●になるようにしましょう。
    「FTP_DIRECTORY」はftp-deployのREMOTE_PATHで読み込んでいるとこですね。ここはFTP接続してどの階層にアップロードするのかを決めています。間違えちゃうとヒドイ場所にアップしてしまう可能性があるので注意深く確認しましょう。

    ymlの構文やBitbucket Pipelinesの必要設定に問題がなければ、入力画面下がこのようにチェック状態になります。
    Commit fileを押すとbitbucket-pipelines.ymlファイルがmasterブランチへコミットされます。
    (この時点では他のブランチへコミットされていないことに注意してください。使いたいブランチへはこのコミットをマージする必要があります。)

    【実践】プッシュ後の挙動を確認してみよう

    masterへコミットされたymlをdevelopブランチにも反映します。

    するとサイドバーのPipelinesから・・・

    リロードしなくてもdevelopブランチへのプッシュを検知して動き出しました。In progressとなっていますね。

    ユーザーアイコン右のInitial Bitbucket Deployments configurationの部分にあたるテキストがリンクになっているので押してみましょう。

    この画面から今現在どのように動作しているのか詳細を追うことができます。
    この画像ではちょうどステップ「install & dev」のyarn installが動いているところですね。
    ステップは真ん中下のPipelineというところです。

    初回はキャッシュがないので時間がかかります。

    問題起きなければこのようにになります。問題があればになります。

    無事デプロイされているかを確認してみましょう。
    サーバーに指定のファイルが上がっていれば成功です!

    サイドバーのDeploymentを選択すると、環境別にどのような状況か確認できます。
    複数の環境へデプロイしている場合はここを確認すればエラーが出ていないか一目で分かります。

    Pipelinesに戻ってもSuccessfulというように状態が確認できます。
    右端にDurationとあります。これがビルド時間であり、料金に関わってくる部分です。
    月に利用できる時間の枠からこの数値が引かれていきます。
    右上のUsageから簡単な利用状況を確認できます。

    キャッシュが正しく機能していれば、右上のCachesから保存されていることが確認できるはずです。ここからキャッシュを削除することもできるので、タスクに問題が起きた際に消してみることも考えられますね。

    他にもSchedulesからはPipelinesを動かす日時を予約できたり、Run Pipelineからプッシュせずとも再度動かせたりします。

    後から設定

    Deploymentや変数は「Repository settings」の「PIPELINES」からいつでも変えられます。

    変数についてのポイントですが、デプロイメント変数 > リポジトリ変数 > ワークスペース変数という強さ順になっています。同じ名前の変数がある場合デプロイメント変数が上書きするというイメージです。
    この記事ではリポジトリ変数のみ使用しましたが、ワークスペース全体で使いたい変数は設定の「Workspace variables」から設定することができます。
    デプロイ環境ごとに設定したい場合は、リポジトリの「Repository settings > Deployments」から設定可能です。ここで環境の名称を変更したり新たに追加することもできます。

    エラー対処と注意点

    エラー対処

    ymlのチェック
    テキストエディタなどでymlを修正していくうちに構文が正しくない状態になっている可能性があります。
    Bitbucketのバリデーターが利用できますのでチェックしてみてください。

    いつまでもprogress状態が続いている
    scriptで終わらないようなタスクを動かしていたり、ftp-deployの各種設定が間違っている可能性があります。
    そのような場合はただちにStopを押して止めましょう。月の利用時間から削られていってしまいます。

    FTPデプロイに失敗する
    ユーザーもパスワードも合っているけど失敗する場合はftpaccessでサーバーに接続できるIPに制限をかけている可能性があります。
    ftp-deployはBitbucket Pipelinesの環境から動かしているので、自分のPCのIPアドレスとは違います。
    下記リンクの下の方にあるValid IP addresses for Bitbucket Pipelines build environmentsの項目にあるIPをftpaccessに追加すると解決するかもしれません。

    注意点

    ビルド時間を気にかける
    ビルド時間がやたら削られていることから発見できるエラーもあります。その都度確認できればいいのですが、ある程度裏側でやっておいてほしい場合はymlで各stepごとにビルド時間の上限を設けることができるmax-timeというものもあります。これはデフォルトで120分になっているのでもっと短くしたほうがいいと思います。

    ftp-deployで--deleteをつけることの注意点
    --deleteはミラーリングになります。つまりGit管理からなくなったファイルは、サーバー側でも削除してしまいます。
    もしデプロイ先のパスを間違えてしまうと、必要なファイルをすべて消される可能性もあります。
    不安な場合は--deleteを使わなくてもいいと思います。その場合は上書き方式になりますのでファイルは残り続けます。

    テストを繰り返して正しい状態を見つける
    最初のうちは思い通りに動かないことは多いので、テスト用のリポジトリなど作成し自分が思い描いている動作をできる構成をひたすらテストしてみましょう。
    実務用のリポジトリでテストすると余計なコミットが大量に生まれるのでオススメしません。

    おしまい

    長めの記事になりましたが、Bitbucketユーザーなら他のCI/CDサービスと連携するよりは手順など簡単ではないでしょうか!
    今回はftp-deployを使用しましたが、他にもrsync-deploygit-ftpというものもあり、挙動は違いますがこれらでもFTPサーバーへ反映できるようです。

    ymlは他のCI/CDサービスでも使用するので、作業を自動化していくために使いこなしたいですね。

    岩淵

    岩淵 Webデザイナー

    2017年にWebデザイナーとしてLYZONに入社。
    現在はアートディレクターとしても活動中。