create-react-app で始めたプロジェクトで、pug を使う

create-react-app をして作ったプロジェクトで、pug を使う方法。
基本的なことなのだが、日本の記事があまりないようなので書くことにする。

pug の書き方

pugという関数でタグ付けされたテンプレートリテラルを使う書き方になる。

テンプレート文字列 - JavaScript | MDN

変数 logo には、{} をつけない。
テンプレートの終了が、.App と同じ位置にないと、
'pug' is not defined no-undef と関係ないエラーが出るのはとても厄介。

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (pug`
    .App
      header.App-header
        img.App-logo(src=logo alt="logo")
    `);
}

export default App;

必要なパッケージ追加

$ npm i -D babel-plugin-transform-react-pug babel-plugin-transform-react-jsx

package.json へ設定を書けるようにする

$ npm run eject

babel の設定を追加

package.json

  "babel": {
    "presets": [
      "react-app"
 +    ],
 +    "plugins": [
 +      "transform-react-pug",
 +      "transform-react-jsx"
    ]
  }

万が一エラーが出た場合、こちらが参考になるかもしれない

javascript - how to use pug templating engine with reactjs - Stack Overflow

Paker で Chef を利用時、ライセンスエラー

Packer で type に chef-solo を指定時、コマンドが失敗するようになった。
これは、ライセンスの承諾が必要になったことが原因。

Chef Infra Client cannot execute without accepting the license
Chef never successfully completed! Any errors should be visible in the
output above. Please fix your recipes so that they properly complete.

`--chef-license accept-silent` を追加する必要がある。

  "provisioners": [
    {
      "type": "chef-solo",
      "cookbook_paths": ["site-cookbooks/"],
      "execute_command": "{{if .Sudo}}sudo {{end}}chef-solo --chef-license accept-silent --no-color -c {{.ConfigPath}} -j {{.JsonPath}}",

    ...

Kubernetes を利用時、大きいデータサイズの画像を複数回POSTするとエラーが出る

Rails を用いて、画像アップロードをするシステムを作っていた時、画像を連続3回アップロードすると502エラーが出る場合があった。

これは、メモリの使用量が大きくなり、pod 内のプロセスがkill されていたことが原因だった。
Ruby on Rails のメモリ消費が大きいというのもあるが、Image Magick を使った画像のリサイズ処理は多少重い処理で、メモリ消費、CPU消費どちらも大きい。

CPUのlimitによって処理が遅くなり、メモリのlimitによってプロセスが終了する。
pod 自体は Terminate されていないように見えたので、原因になかなか気がつかなかった。


ログを見るとこのようになっている。

Memory cgroup out of memory: Kill process 1117697 (bundle) score 1802 or sacrifice child Killed process 1117697 (bundle) total-vm:950788kB, anon-rss:340148kB, file-rss:14348kB, shmem-rss:0kB
[Note] Aborted connection 36967 to db: 'hoge_db' user: 'hoge' host: 'cloudsqlproxy~34.00.100.04' (Got an error reading communication packets)
[error] 7#7: *7447 upstream prematurely closed connection while reading response header from upstream, client: 10.146.15.198, server: _, request: "POST /admin/datas/1/ HTTP/1.1", upstream: "http://10.0.15.103:3000/admin/datas/1/", host: "***.com", referrer: "https://***.com/datas/1/edit/"
Client closed local connection on 10.40.4.24:3306
[error] 7#7: *5400 client intended to send too large body: 2328315 bytes, client: 10.40.4.181, server: _, request: "GET /errors/5xx.html HTTP/1.0", host: "cluster-ip-stg-hoge", referrer: "https://***/datas/1/edit/"

CPU は limit を超えても、killされず処理が遅くなるだけだが、memory は kill されるのは間違えないように。

          resources:
            requests:
              cpu: 50m
              memory: 230Mi
            limits:
              cpu: 500m
              memory: 800Mi

Android で スタティックライブラリを利用する

Android の開発時に、C++などで作られた 静的ライブラリ〜.a や、共有ライブラリ〜.so を利用したい時。

まず、ビルド設定方法は1つではない。CMakeLists.txt を使う方法、Android.mk を使う方法と、やり方が選べる。

また、 so ファイルは、アプリケーションコードからロードできるが、 a ファイルは直接ロードできず、so を経由して読み込むことになる。

選択する言語は、Java・Kotolin どちらでも全く問題ない。
ドキュメントだけでは理解できない部分があったのでまとめた。

公式のドキュメントはこちら。
プロジェクトへの C / C++ コードの追加  |  Android Developers

C、C++側の書き方はこちらを参照。
Java Native Interface仕様の目次

Android NDK環境のインストール

Android Studio を起動し、
Android SDK Manager > SDK Toolsタブ と進む。
CMake、LLDB、NDK を選択しインストールする。

プロジェクト作成

ここからは通常通り新規作成をするが、Choose your project で、Native C++ を選択する。
他のサイトでは、Include C++ support のチェックを入れるように書かれているが、その設定はもう無くなっており、必要ない。

プロジェクト内ファイルの説明

cpp フォルダの中に、CMakeLists.txt と、native-lib.cpp ファイルがある。
この2ファイルをいじるだけで、ビルドし、理解する分には十分です。

CMakeLists.txt の書き方

関数の引数は、スペースや改行で区切るイメージ。

共有ライブラリを作成時(C++をそのまま呼び出す場合)

CMakeLists.txt

# ライブラリのルート
set( LIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. )
# ライブラリの出力先
# ANDROID_ABIで、対応するプロセッサごとにフォルダを分ける
set( OUTPUT_DIR ${LIB_ROOT}/lib/${ANDROID_ABI} )

# ライブラリのソースファイル
# add_library( ライブラリ名 SHARED ソースファイル )
add_library( native-lib SHARED native-lib.cpp )

# 共有ライブラリ 出力先を指定
# set_target_properties( ライブラリ名 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR} )
set_target_properties( native-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR} )

# ライブラリを変数に定義
# 必須ではないが、このような書き方が出来る
# find_library( 定義名 ライブラリ名... )
find_library( log-lib log )

# 依存ファイルのリンク
# native-lib.cpp の中で、${log-lib} を利用してプログラムをしている場合
# target_link_libraries( ライブラリ名 依存ファイル... )
target_link_libraries( native-lib ${log-lib} )
静的ライブラリを作成する時

CMakeLists.txt

# ライブラリのルート
set( LIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. )
# ライブラリの出力先設定
set( OUTPUT_DIR ${LIB_ROOT}/lib/${ANDROID_ABI} )

# ライブラリのソースファイル
# add_library( ライブラリ名 STATIC ソースファイル )
add_library( native-lib STATIC native-lib.cpp )

# 静的ライブラリ 出力先を指定
# set_target_properties( ライブラリ名 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR} )
set_target_properties( native-lib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR} )

build.gradle
リンクしない STATIC ライブラリはビルドされないため、常にビルドさせる設定

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                # targets 'ライブラリ名'
                targets 'native-lib'
            }
        }
    }
}
共有ライブラリを利用する時

読み込めばC++の関数が呼び出せる。

CMakeLists.txt

# ライブラリのルート
set( LIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. )
# ライブラリの出力先設定
set( OUTPUT_DIR ${LIB_ROOT}/lib/${ANDROID_ABI} )

# ライブラリが外部参照である事を指定
add_library( native-lib SHARED IMPORTED )
# ライブラリファイルの位置を指定
set_target_properties( native-lib PROPERTIES IMPORTED_LOCATION ${OUTPUT_DIR}/libanylib.so )

*.java

class Hoge {
    static {
        System.loadLibrary("native-lib");
    }
}
静的ライブラリを利用する時

流れとしては、一度 静的ライブラリを元に、共有ライブラリを作成し、共有ライブラリを読み込むことになる。
なので、上記の方法を全て把握する必要がある。

CMakeLists.txt

# ライブラリのルート
set( LIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. )
# ライブラリの出力先
set( OUTPUT_DIR ${LIB_ROOT}/lib/${ANDROID_ABI} )

# ライブラリが外部参照である事を指定
add_library( anylib STATIC IMPORTED )
# ライブラリファイルの位置を指定
set_target_properties( anylib PROPERTIES IMPORTED_LOCATION ${OUTPUT_DIR}/libanylib.a )

# 共有ライブラリの生成
add_library( native-lib SHARED native-lib.cpp )
# 共有ライブラリ 出力先を指定
set_target_properties( native-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR} )

# ライブラリのインクルードファイルの参照先
target_include_directories( native-lib PRIVATE ${LIB_ROOT}/include )

# ライブラリを変数に定義
find_library( log-lib log )

# 依存ファイルのリンク
# native-lib.cpp の中で、anylibと、${log-lib} を利用してプログラムをしている場合
target_link_libraries(
        native-lib
        anylib
        ${log-lib} )

*.java

class Hoge {
    static {
        System.loadLibrary("native-lib");
    }
}

Google Cloud Storage 静的ウェブサイトのホスティング 時、 index.html へリダイレクトされる

静的ウェブサイトのホスティング  |  Cloud Storage  |  Google Cloud

gcs を利用し、 websiteホスティングした時に、
スラッシュ無しのパスへアクセスしたとき、/index.html が存在する場合、/index.html が付加されたURLへリダイレクトしてしまう。
しかも、CNAME (正確には Host ヘッダー)に指定した Host でリダイレクトする。

Nginx などで Proxy した場合は、特に問題となる。

そのため、リダイレクトが発生した場合にRedirect を書き換える必要がある。
~ は正規表現を利用する宣言。
example.com を CNAME とした場合の設定。

    set $proto $scheme;
    if ($http_x_forwarded_proto = "https")  { set $proto "https"; } # ELB, GCLB
    if ($http_cloudfront_forwarded_proto = "https")  { set $proto "https"; } # CloudFront

    # ...
 
    resolver 8.8.8.8 valid=60s;

    proxy_pass http://c.storage.googleapis.com;
    proxy_redirect ~example.com\/(.*)\/index.html $proto://$http_host/$1/$is_args$args; # google cloud storage

    proxy_set_header Host example.com;

CloudFront の Origin に API Gateway を入れた場合に リダイレクトが発生する

API GatewayAPI を作った場合など、そのURLをCloudFrontのOriginに指定したい時がある。

その時に、Origin Protocol Policy を、HTTP Only にしていると、リダイレクトが発生する。HTTPS Onlyにする必要がある。

Vue のファイル出力先を変更する

Vue の デフォルトの出力フォルダは、dist フォルダになっているが、他のフレームワークと共に使うような時、出力先を変えたい場合がある。
さらに出力先を公開ディレクトリ(/public)のサブディレクトリ(/public/webview)にしたい場合の vue.config.js の設定方法。

/
├── frontend
│   ├── vue.config.js
│   ├── babel.config.js
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   └── src
└── public
    └── webview

$ vue.config.js

module.exports = {
  outputDir:'../public/webview', // ファイルの出力先ルート
  publicPath: './webview', // index.html などの出力されるファイルに書き込まれる、ルートとなるディレクトリ
  pages: { // 特に今回書く必要はない
    index: {
      entry: 'src/main.js',
      template: 'public/index.html',
      filename: 'index.html', // webview フォルダからの相対パス
    }
  }
}