湯飲みで盆栽の鉢を作る
材料
- ヤスリ:角が丸い、100円ショップ(ダイアモンド)
- 湯飲み:100円ショップ
- ネジ、釘:家にあるもの
- ゴムハンマー:家にあるもの(100円ショップ)
湯飲みの選別は底の厚みが薄い物を探しました。
穴あけ
他のネットの記事を読むと湯飲みの中に砂を入れて釘とハンマーで穴を開ける内容がありましたが、砂もないし面倒かなと思いまして底が薄い物に協力なテープを貼ってやりました。
間違えて割れるのではないか心配もしましたが、何と一発で成功しました。
穴を広げる
盆栽用の鉢は普通の物より水捌けをよくするように穴が大きいです。今回ネジで開けた穴は小さかったのでヤスリで穴を広げました。
また、その横にも水が流れるように三つのボコを作りました。もっと大きくした方が良いかと思いながら、このくらいにするのも大変だったので手を上げました。
完成
以前買った松の寄せ植えから1本分けて使ってみました。
GAS(Google Apps Script)を利用してQiitaとNoteの記事をSlackへ送信
事前準備
SlackのAPIを使うためにTokenが必要です。基本的に二つの物があります。
Legacy token
workspaceの管理用のTokenなので、権限のレベルが高いので他人に共有することなら良く考えた方が良いです。
参考:https://qiita.com/ykhirao/items/0d6b9f4a0cc626884dbb
App Token
作成したアプリに制限されるのでそのアプリを追加したチャネルのみ影響されます。
参考:https://qiita.com/ykhirao/items/3b19ee6a1458cfb4ba21
今回は自分はアプリを作成して、そのアプリ対象のチャネルに登録し利用しました。
GAS作成
Google Driveで「新規」 > 「その他」 > 「Google App Script」を選択して、新しいGASを作成します。
完成ソース
いきなり完成ソースを共有します。その後に細かい説明します。
// Slack定数 var SLACK_CANNEL = "SlackのChannel"; var SLACK_URL = 'https://slack.com/api/chat.postMessage'; var SLACK_TOKEN = 'xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; // RSS定数 var FORMAT_TITLE = "%sに新しい投稿があります。"; var FORMAT_QIITA_URL = "http://qiita.com/organizations/%s/activities.atom"; var FORMAT_QIITA_AUTHOR_URL = "http://qiita.com/%s"; var FORMAT_NOTE_URL = "https://note.com/%s/rss"; var FORMAT_NOTE_AUTHOR_URL = "https://note.com/%s"; var KIND_QIITA = "Qiita"; var KIND_NOTE = "Note"; // 5分をミリ秒へ変換したもの var LIMIT_TIME_LAG = 300000; // 更新通知を行うユーザー名 var organizations = [ {kind: KIND_QIITA, key: "Qiitaの企業のURLキー"}, {kind: KIND_NOTE, key: "NoteのURLキー"} ]; var exceptions = []; function main() { // 記事をSlackへ送信 for (var k = 0; k < organizations.length; k++) { parseXml(organizations[k]); } // Exceptionを出力 if (exceptions.length > 0) { for(var i = 0; i < exceptions.length; i++) { var e = exceptions[i]; Logger.log(e); } throw new Error('RSSの通信のエラーがありました。ログを確認してください。'); } } function parseXml(rssInfo) { var url = null, authorUrlFormat = null; var blocks = [], posts = []; var msgTitle = Utilities.formatString(FORMAT_TITLE, rssInfo.kind); if (rssInfo.kind == KIND_QIITA) { // Qiitaの場合 // フィードURL url = Utilities.formatString(FORMAT_QIITA_URL, rssInfo.key); // 作成者URL authorUrlFormat = FORMAT_QIITA_AUTHOR_URL; // Blockの初期(先頭)設定 blocks = [ { type: "section", text: { type: "mrkdwn", text: msgTitle + "\n\n*新しい記事リスト*" } }, { "type": "divider" } ]; posts = parseQiita(url); } else if (rssInfo.kind == KIND_NOTE) { // Noteの場合 // フィードURL url = Utilities.formatString(FORMAT_NOTE_URL, rssInfo.key); // 作成者URL authorUrlFormat = FORMAT_NOTE_AUTHOR_URL; // Blockの初期(先頭)設定 blocks = [ { type: "section", text: { type: "mrkdwn", text: msgTitle + "\n\n*新しい記事リスト*" } }, { "type": "divider" } ]; posts = parseNote(url); } if (posts.length > 0) { for (var i = 0; i < posts.length; i++) { var post = posts[i]; msgTitle = msgTitle + " <" + post.url + ">"; blocks.push( { type: "section", text: { type: "mrkdwn", text: "<" + Utilities.formatString(authorUrlFormat, post.authorKey) + "|@" + post.authorName + ">から「" + post.title + "」が投稿されました。\nLink: <" + post.url + ">" } } ); } // Blockの最後設定 blocks.push( { "type": "divider" }); var tmp = JSON.stringify(blocks); // Logger.log(tmp); var payload = { token: SLACK_TOKEN, channel: SLACK_CANNEL, text: msgTitle, unfurl_links: true, username: "Wiz Robot", icon_url: "https://inspirelabs.pl/wp-content/uploads/2018/01/robot.png", blocks: JSON.stringify(blocks) }; var params = { 'method': 'post', 'payload': payload, 'muteHttpExceptions': true }; try { var response = UrlFetchApp.fetch(SLACK_URL, params); } catch(e) { exceptions.push(e); } var responseCode = response.getResponseCode(); if (responseCode != 200) { var responseBody = response.getContentText(); exceptions.push(Utilities.formatString("Request failed. Expected 200, got %d: %s", responseCode, responseBody)); } } } function parseQiita(url) { // 上記URLにGETリクエスト、getContentText()でxmlを取得 var response = UrlFetchApp.fetch(url, {'muteHttpExceptions': true}); // 取得失敗した場合 var responseCode = response.getResponseCode(); if (responseCode != 200) { var responseBody = response.getContentText(); exceptions.push(Utilities.formatString("Request failed. Qiita Expected 200, got %d: %s", responseCode, responseBody)); return []; } // xmlを取得 var xml = response.getContentText(); // 取得したxmlを文書化 var document = XmlService.parse(xml); // ドキュメントのルート要素を抽出 var root = document.getRootElement(); // 引数のURLを用いてNameSpaceを生成します。(おそらくフォーマット指定?) var atom = XmlService.getNamespace("http://www.w3.org/2005/Atom"); // エントリー記事を取得 var entries = root.getChildren("entry", atom); // Logger.log(entries); var returnArray = []; for (var i = 0; i < entries.length; i++) { // 最新記事を取得 var entry = entries[i]; var id = entry.getChild("id", atom).getText(); var pos = id.indexOf("/"); var entrieId = id.substr(pos+1); // 記事情報の取得 var postUrl = entry.getChild("url", atom).getText(); var title = entry.getChild("title", atom).getText(); var author = entry.getChild("author", atom).getChild("name", atom).getText(); // 記事の更新日時を取得 var publishTime = entry.getChild("published", atom).getText(); var nowTime = Utilities.formatDate(new Date(), "Asia/Tokyo", "yyyy-MM-dd'T'HH:mm:ss"); // 更新があった場合[1分以内に更新があったもの] if (Date.parse(nowTime) - Date.parse(publishTime) <= LIMIT_TIME_LAG) { returnArray.push({authorKey: author, authorName: author, title: title, url: postUrl}); } } return returnArray; } function parseNote(url) { // 上記URLにGETリクエスト、getContentText()でxmlを取得 var response = UrlFetchApp.fetch(url, {'muteHttpExceptions': true}); // 取得失敗した場合 var responseCode = response.getResponseCode(); if (responseCode != 200) { var responseBody = response.getContentText(); exceptions.push(Utilities.formatString("Request failed. Note Expected 200, got %d: %s", responseCode, responseBody)); return []; } // xmlを取得 var xml = response.getContentText(); // 取得したxmlを文書化 var document = XmlService.parse(xml); // XMLのitem配下のデータを取得 var items = document.getRootElement().getChildren('channel')[0].getChildren('item'); // Logger.log(items); // 引数のURLを用いてNameSpaceを生成します。 var node = XmlService.getNamespace("https://note.com"); var returnArray = []; for (var i = 0; i < items.length; i++) { // 最新記事を取得 var item = items[i]; // 記事情報の取得 var postUrl = item.getChild("link").getText(); var title = item.getChild("title").getText(); var authorKey = item.getChild("link").getText().match(/https:\/\/note.com\/([a-z0-9_]*?)\/.*?/)[1]; var authorName = item.getChild("creatorName", node).getText(); // 記事の更新日時を取得 var publishTime = item.getChild("pubDate").getText(); var nowTime = Utilities.formatDate(new Date(), "Asia/Tokyo", "yyyy-MM-dd'T'HH:mm:ss"); // 更新があった場合[1分以内に更新があったもの] if (Date.parse(nowTime) - Date.parse(publishTime) <= LIMIT_TIME_LAG) { returnArray.push({authorKey: authorKey, authorName: authorName, title: title, url: postUrl}); } } return returnArray; }
SLACK_CHANNEL
ブラウザーだとURLが「https://xxxxxx.slack.com/archives/YYYYYYYYY」の場合、「YYYYYYYYY」がチャネルIDです。
アプリの場合は対象チャネルでマウスの右ボタンをクリックすると以下のメニューが表示されます。
このメニューの「リンクをコピー」をクリックするとURLが見えるので同じく一番後ろの「YYYYYYYYY」を取得します。
SLACK_TOKEN
上記「事前準備」のどちらかのTokenを設定します。
FORMAT_QIITA_URL
Qiitaの企業向けのRSS配信するURLパターンです。「%s」のところに企業URL「https://qiita.com/organizations/xxxxx」の一番後ろの「xxxxx」が入る感じです。この「xxxxx」が「Qiitaの企業のURLキー」部分に入ります。
FORMAT_QIITA_AUTHOR_URL
RSSをパーシングして作成者の情報から実際の作成者のURLを作るURLパターンです。
FORMAT_NOTE_URL
FORMAT_QIITA_URLと同様にNoteのRSS配信するURLパターンです。「http://note.com/」と「/rss」の間の文字が「NoteのURLキー」部分に入ります。
FORMAT_NOTE_AUTHOR_URL
FORMAT_QIITA_AUTHOR_URLと同様に作成者の情報から実際の作成者のURLを作るURLパターンです。
トリガーの設定
「編集」 > 「現在のプロジェクトのトリガー」を選択してトリガー作成を行います。
時間設定は分単位で「LIMIT_TIME_LAG」の設定と合わせて5分に設定します。
記憶しておくべき
権限(SCOPE)
アプリのアイコンや表示名の変更する以下のソースがありますが、こちら基本的に必要な権限(SCOPE)があります。
参考:https://api.slack.com/methods/chat.postMessage
OGP カード
「unfurl_links: true」にしてもOGP情報が表示されなくて、調べた結果textに設定したもので6個以上は表示されないようです。そのために以下の部分を入れました。
msgTitle = msgTitle + " <" + post.url + ">";
その後にmsgTitleを設定します。このtextが実際表示されるのはOS(?)の通知に表示されるだけです。
var payload = { token: SLACK_TOKEN, channel: SLACK_CANNEL, text: msgTitle, unfurl_links: true, username: "Robot", icon_url: "https://XXXXXXXX/robot.png", blocks: JSON.stringify(blocks) };
Qiitaのエラー
実際運営して見るとQiitaのRSSで以下のようなエラーが出ていました。
Exception: 使用できないアドレス: http://qiita.com/organizations/xxxxxx/activities.atom
5分単位でトリガーを設定したので、負荷?が掛かったのか良く分からないですがその後の処理が実行されなくなるので、以下の部分で対応しました。
// 上記URLにGETリクエスト、getContentText()でxmlを取得 var response = UrlFetchApp.fetch(url, {'muteHttpExceptions': true}); // 取得失敗した場合 var responseCode = response.getResponseCode(); if (responseCode != 200) { var responseBody = response.getContentText(); exceptions.push(Utilities.formatString("Request failed. Qiita Expected 200, got %d: %s", responseCode, responseBody)); return []; } // xmlを取得 var xml = response.getContentText();
「muteHttpExceptions: true」に設定した後にresponseCodeで判定してエラーのログを出力します。
改善すべきところ
Function的な感じ
全然OOPではないことに完成ソースを見るとめがチクチクして来ました。ソースを管理する上にlocalでTSで作成して管理、実行時にBuildする方法が欲しいですね。
Composerの実行速度が遅い
概要
phpenvで開発環境を構成して使っていて、いつからか分からないがComposerのパッケージのダウンロードを含めて動作が遅くなりました。色々資料を調べた内容を纏めてみました。
Composerのアップデート
バージョンが1.2.X系であれば1.3.X系にバージョンアップする事をオススメします。composer自体の更新は下記のコマンドで実行出来ます。
$ composer self-update
packagistのミラーサーバを日本国内のサーバに変更
原因としては以下の三つだようです。
- packagist.orgが日本から遠い
- composerのアーキテクチャ的に、小さなjsonファイルを少しずつダウンロードするため、ネットワークの遅延がもろに影響する
- composerがfile_get_contentsでjsonをダウンロードしていて、どうやらKeep-Aliveを使っていないし、並列ダウンロードもしていない
日本国内のサーバに変更する事でダウンロードの高速化を図ります。
% composer config -g repos.packagist composer https://packagist.jp
戻すときは以下のコンマンドを実行します。
% composer config -g --unset repos.packagist
Composerプラグインによる高速化
prestissimoというcomposerプラグインを利用する方法です。これは「packagistのミラーサーバを日本国内のサーバに変更」に記載した下記の問題を解消する為のプラグインのようです。
% composer global require hirak/prestissimo
9.改善された開発環境を作成
こちらで公開しております。
前回の問題点
公開した「8.開発環境の完成」で複数のphp-fpmが一個のCentosに入ることで以下の問題点がありました。
- 複数のphp-fpmのphpenvを利用したBuildが時間が掛かるし、Build設定などを調査して対応しないと行けない
- 複数のphp-fpmをインストールするために色んなライブラリーやモジュールをインストールが必要でCentosが重くなる
- 複数のphp-fpmで必要なライブラリーやモジュールのバージョンの違いでエラーが出る場合があって対応する必要がある
- 必要がなくなったバージョンや新しくバージョンのインストールでウェブサーバーのDockerfileを修正・ビルドする場合がある
解決案
複数のphp-fpmを各自コンテイナーで動かしてサービスをしてCentosのbuild時間や負荷を減らしました。
ウェブ(Centos)
php-fpmを複数インストールするためにanyenvやライブラリー部分を全部削除して最小限でDockerfileを作りました。
FROM centos:7 MAINTAINER kugyon.i <fatty.rabbit.75@gmail.com> # Declare Arguments ARG SERVER_NAME=local # TimeZoneの設定 RUN rm -rf /etc/localtime && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime RUN echo "LC_ALL=ja_JP.UTF-8" >> /etc/environment && \ echo "LC_CTYPE=ja_JP.UTF-8" >> /etc/environment && \ echo "LANG=ja_JP.UTF-8" >> /etc/environment RUN localedef -f UTF-8 -i ja_JP /usr/lib/locale/ja_JP.UTF-8 RUN localedef -f UTF-8 -i ja_JP ja_JP # yumアップデート RUN yum -y update RUN yum -y remove httpd httpd.x86_64 httpd-tools.x86_64 # install leatest apache2.4.* RUN yum install -y epel-release && \ yum -y install https://repo.ius.io/ius-release-el7.rpm && \ sed -i -e "s/enabled *= *1/enabled=0/g" /etc/yum.repos.d/ius.repo && \ sed -i -e "s/enabled *= *1/enabled=0/g" /etc/yum.repos.d/epel.repo RUN yum --enablerepo=epel -y install nghttp2 && \ yum -y install epel-release && \ yum -y install mailcap system-logos && \ yum -y install openssl wget && \ # Installing packages required for httpd 2.4.41 wget http://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/b/brotli-1.0.7-5.el7.x86_64.rpm && \ rpm -Uvh brotli-1.0.7-5.el7.x86_64.rpm && \ yum -y install brotli && \ yum -y --disablerepo=base,extras,updates --enablerepo=ius install httpd mod_ssl && \ yum clean all RUN sed -i -e "s|LoadModule mpm_prefork_module modules/mod_mpm_prefork.so|#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so|" /etc/httpd/conf.modules.d/00-mpm.conf && \ sed -i -e "s|#LoadModule mpm_event_module modules/mod_mpm_event.so|LoadModule mpm_event_module modules/mod_mpm_event.so|" /etc/httpd/conf.modules.d/00-mpm.conf RUN sed -i -e "s|LoadModule mpm_prefork_module modules/mod_mpm_prefork.so|#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so|" /etc/httpd/conf/httpd.conf && \ sed -i -e "s|#LoadModule mpm_event_module modules/mod_mpm_event.so|LoadModule mpm_event_module modules/mod_mpm_event.so|" /etc/httpd/conf/httpd.conf # make SSL server certificate RUN mkdir -p /etc/ssl/private && \ openssl req -new -newkey rsa:2048 -nodes -out /etc/ssl/private/server.csr -keyout /etc/ssl/private/server.key -subj "/C=JP/ST=TOKYO/L=SHIBUYA/O=Wiz Inc./OU=/CN=*.${SERVER_NAME}" && \ openssl x509 -days 36500 -req -signkey /etc/ssl/private/server.key -in /etc/ssl/private/server.csr -out /etc/ssl/private/server.crt COPY conf.d/vhosts.conf /etc/httpd/conf.d/vhosts.conf RUN systemctl enable httpd
追加コンテイナー
複数php-fpm
出来ればdockerhubであるイメージをそのまま利用したかったですが、使用するモジュールによって設定が必要なのでDockerfileを作成しました。以下の7.1のDockerfileです。
FROM php:7.1-fpm ENV DEBIAN_FRONTEND noninteractive ENV DEBCONF_NOWARNINGS yes RUN apt-get update RUN apt-get install -y wget libjpeg-dev libfreetype6-dev RUN apt-get install -y gcc make libjpeg-dev zlib1g-dev mariadb-client libfreetype6-dev libjpeg62-turbo-dev libpng-dev libmcrypt-dev libzip-dev \ && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ && docker-php-ext-install -j$(nproc) gd zip mysqli pdo_mysql bcmath RUN pecl install xdebug \ && docker-php-ext-enable xdebug #Composer install COPY --from=composer /usr/bin/composer /usr/bin/composer COPY php.ini /usr/local/etc/php/ ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin
busybox
速度の改善で入れました。実際どのくらい影響されているかは計測しておりません。
mailhog
メールsmtpのコンテイナーです。テスト環境でテストメールの確認で便利です。
完成docker-compose.yml
version: "3" services: datastore: image: busybox container_name: datastore volumes: - ../web_root:/var/www:rw - ./dba/mysql:/var/lib/mysql web: image: web build: context: ./web dockerfile: Dockerfile container_name: web ports: - "80:80" - "443:443" command: /sbin/init volumes: - ../web_root:/var/www:rw - ./web/conf.d/extra:/etc/httpd/conf.d/extra:ro tty: true stdin_open: true privileged: true restart: always external_links: - "php7.1-fpm" - "php7.2-fpm" - "php7.3-fpm" - "php7.4-fpm" logging: options: max-size: 5m max-file: "5" networks: local-server-net: ipv4_address: 172.16.0.2 dba: image: dba-mariadb build: context: ./dba dockerfile: Dockerfile container_name: dba ports: - "3306:3306" volumes: - ./dba/my.cnf:/etc/my.cnf - ./dba/mysql:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: 12345678 MYSQL_ROOT_HOST: "172.16.0.%" TZ: Asia/Tokyo restart: always logging: options: max-size: 5m max-file: "5" networks: local-server-net: ipv4_address: 172.16.0.3 dns: image: andyshinn/dnsmasq container_name: dns ports: - "53:53/udp" - "53:53/tcp" volumes: - ./dns/dnsmasq.conf:/etc/dnsmasq.conf - ./dns/hosts-dnsmasq:/etc/hosts-dnsmasq restart: always cap_add: - NET_ADMIN networks: local-server-net: ipv4_address: 172.16.0.4 mailhog: image: mailhog/mailhog:latest container_name: mailhog ports: - "1025:1025" - "8025:8025" networks: local-server-net: ipv4_address: 172.16.0.5 php7.1: image: fpm-php:7.1 build: context: ./php-fpm dockerfile: 7.1_Dockerfile container_name: php7.1-fpm volumes: - ../web_root:/var/www external_links: - "dba" ports: - '9000' networks: local-server-net: ipv4_address: 172.16.0.6 php7.2: image: fpm-php:7.2 build: context: ./php-fpm dockerfile: 7.2_Dockerfile container_name: php7.2-fpm volumes: - ../web_root:/var/www external_links: - "dba" ports: - '9000' networks: local-server-net: ipv4_address: 172.16.0.7 php7.3: image: fpm-php:7.3 build: context: ./php-fpm dockerfile: 7.3_Dockerfile container_name: php7.3-fpm volumes: - ../web_root:/var/www external_links: - "dba" ports: - '9000' networks: local-server-net: ipv4_address: 172.16.0.8 php7.4: image: fpm-php:7.4 build: context: ./php-fpm dockerfile: 7.4_Dockerfile container_name: php7.4-fpm volumes: - ../web_root:/var/www external_links: - "dba" ports: - '9000' networks: local-server-net: ipv4_address: 172.16.0.9 networks: local-server-net: driver: bridge ipam: driver: default config: - subnet: 172.16.0.0/24
GithubのActionsでRsyncを利用したデプロイ
前提条件
要求内容
- Rsyncでgitのソースをサーバーにアップ
- ソースのデプロイした後に簡単にサーバーでコンマンドを実行
- Staging・本番環境の関係なくデプロイはbranchのpushで自動デプロイ
事前設定
Githubの設定
利用するレポジトリの「Settings > Secrets」に入って、サーバーへ接続情報を登録します。
SSH用の鍵を登録
SSH用のユーザー名を登録
SSH用のHostを登録
Actionsのworkflowを作成
対象リポジトリの「Actions」に入って新しくTempleteを利用して作成しても良いです。
トリガー
要求内容にも書いてますが、簡単なプロジェクトを想定しているのでStaging・本番環境の関係なくデプロイはbranchのpushで自動デプロイします。
name: deploy dev on: push: branches: - deploy/*
もし本番を手動でしたい場合は「GithubのActionsでLaravelのデプロイ」をご参考してください。
環境変数の定義
二つのブランチ(Staging、Production)についての変数を設定します。
steps: - uses: actions/checkout@v2 - name: Set staging env if: github.ref == 'refs/heads/deploy/staging' run: | echo ::set-env name=RSYNC_SOURCE::/ echo ::set-env name=RSYNC_TARGET::Staging用のDirecotry - name: Set production env if: github.ref == 'refs/heads/deploy/pro' run: | echo ::set-env name=RSYNC_SOURCE::/ echo ::set-env name=RSYNC_TARGET::本番用のDirecotry
Rsyncでソースのアップ
今回は「trendyminds/github-actions-rsync」を利用しますが、「ubuntu-latest」を利用するので直接に書いても大丈夫です。
- name: Deploy by rsync uses: trendyminds/github-actions-rsync@master with: RSYNC_OPTIONS: -avzr --delete --exclude-from=.rsyncignore RSYNC_TARGET: $RSYNC_TARGET RSYNC_SOURCE: $RSYNC_SOURCE env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_USERNAME: ${{ secrets.SSH_USERNAME }} SSH_HOSTNAME: ${{ secrets.SSH_HOSTNAME }}
「.rsyncignore」のファイルで対象外のファイルを定義して利用しています。
SSH接続情報の設定
速度の改善するためにCacheをする方法を入れました。情報の設定はCacheがない場合のみに作成する感じです。
- name: Cache ssh config id: cache-ssh-config uses: actions/cache@v1 with: path: /home/runner/.ssh key: ${{ runner.os }}-ssh-config-${{ hashFiles('/home/runner/.ssh/private-key-for-deploy') }} restore-keys: | ${{ runner.os }}-ssh-config- - name: Make ssh config if: steps.cache-ssh-config.outputs.cache-hit != 'true' run: | mkdir -p /home/runner/.ssh touch /home/runner/.ssh/private-key-for-deploy echo "${{ secrets.SSH_PRIVATE_KEY }}" > /home/runner/.ssh/private-key-for-deploy chmod 600 /home/runner/.ssh/private-key-for-deploy ssh-keyscan ${{ secrets.SSH_HOSTNAME }} >> ~/.ssh/known_hosts
SSHを利用してサーバーでコマンドを実行
こちらについては「appleboy/ssh-action」を使うかと思いましたが、Actionsの環境変数をscript設定に反映するのが上手く行かなかったので直接書くことにしました。
上手く行かなかった定義です。
- name: Multiple command uses: appleboy/ssh-action@master with: host: ${{ secrets.SSH_HOSTNAME }} username: ${{ secrets.SSH_USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} envs: RSYNC_TARGET script_stop: false script: | cd $RSYNC_TARGET
こちらが直接書いた内容です。
- name: Make shell run: | touch ./ssh-script.sh echo "cd $RSYNC_TARGET" >> ssh-script.sh echo "ls -al" >> ssh-script.sh cat ssh-script.sh - name: Excute shell run: | ssh -i ~/.ssh/private-key-for-deploy ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOSTNAME }} 'bash -s' < ./ssh-script.sh
完成された定義
name: deploy dev on: push: branches: - deploy/* jobs: deploy: name: deploy runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set staging env if: github.ref == 'refs/heads/deploy/staging' run: | echo ::set-env name=RSYNC_SOURCE::/ echo ::set-env name=RSYNC_TARGET::Staging用のDirectory - name: Set production env if: github.ref == 'refs/heads/deploy/pro' run: | echo ::set-env name=RSYNC_SOURCE::/ echo ::set-env name=RSYNC_TARGET::本番用のDirectory - name: Deploy by rsync uses: trendyminds/github-actions-rsync@master with: RSYNC_OPTIONS: -avzr --delete --exclude-from=.rsyncignore RSYNC_TARGET: $RSYNC_TARGET RSYNC_SOURCE: $RSYNC_SOURCE env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_USERNAME: ${{ secrets.SSH_USERNAME }} SSH_HOSTNAME: ${{ secrets.SSH_HOSTNAME }} - name: Cache ssh config id: cache-ssh-config uses: actions/cache@v1 with: path: /home/runner/.ssh key: ${{ runner.os }}-ssh-config-${{ hashFiles('/home/runner/.ssh/private-key-for-deploy') }} restore-keys: | ${{ runner.os }}-ssh-config- - name: Make ssh config if: steps.cache-ssh-config.outputs.cache-hit != 'true' run: | mkdir -p /home/runner/.ssh touch /home/runner/.ssh/private-key-for-deploy echo "${{ secrets.SSH_PRIVATE_KEY }}" > /home/runner/.ssh/private-key-for-deploy chmod 600 /home/runner/.ssh/private-key-for-deploy ssh-keyscan ${{ secrets.SSH_HOSTNAME }} >> ~/.ssh/known_hosts - name: Make shell run: | touch ./ssh-script.sh echo "cd $RSYNC_TARGET" >> ssh-script.sh echo "ls -al" >> ssh-script.sh cat ssh-script.sh - name: Excute shell run: | ssh -i ~/.ssh/private-key-for-deploy ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOSTNAME }} 'bash -s' < ./ssh-script.sh