9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Cloud Functions for Firebase に kotlin で書いたコードをデプロイする

Posted at

kotlin 1.2 がリリースされて kotlin を js にコンパイルできるようになりました。というわけで Cloud Functions for Firebase に kotlin で書いたコードをコンパイルしてデプロイします。

ファイル構成

自分の場合、

Full stack kotlin プロジェクトで開発をはじめて1週間くらいたった感想 - Qiita

で作っているプロジェクトなので、次のようになっています。これから出てくるパスは適宜読み替えてください。

├── firebase
│   ├── build.gradle
│   ├── firebase.iml
│   ├── firebase.json
│   └── functions
│       ├── build
│       │   ├── classes
│       │   ├── libs
│       │   ├── public
│       │   └── tmp
│       ├── replace_depends.sh
│       ├── build.gradle
│       ├── functions.iml
│       ├── index.js
│       ├── package.json
│       └── src
│           └── main
│                └── kotlin
├── common-js
│   ├── build
│   │   ├── classes
│   │   │   └── kotlin
│   │   ├── libs
│   │   │   └── common-js.jar
│   │   └── tmp
│   │       └── jar
│   ├── build.gradle
│   ├── common-js.iml
│   └── src
│       └── main
│           └── kotlin
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle

プライベートモジュールをデプロイできない問題

先に注意点です。

実はkotlinをjsにコンパイルしてもnpm kotlinに依存しているだけであれば特に難しいことはないのですが、それ以外のkotlin関連のnpmパッケージや自分で作ったモジュールに依存している場合、 Cloud Functions には private なモジュールをデプロイできないという制限のため、デプロイ方法がちょっと難しくなります。

具体的には、コンパイルされたコードの中にrequire('kotlinx-serialization-runtime-js')と言うような、npmに含まれないモジュールへの依存関係が宣言されてしまい、その結果、関数の実行時エラーとなります。

これらを解決する必要があります。

実はコンパイルしたときに依存しているモジュールのソースコードはbuild/classes/kotlin/main/dependencies以下に展開されます。展開されたファイルを依存先として解決するようにコンパイルされたコードを改変することで、実行時エラーの回避が可能です。

今回は次のスクリプトを用意しました。

firebase/functions/replace_depends.sh
# !/usr/bin/env bash
set -eu
script_dir_path=$(dirname $(greadlink -f $0))
build_dir="${script_dir_path}/build/classes/kotlin/main/dependencies"

modules=(kotlin common-js kotlinx-serialization-runtime-js)
for ((i = 0; i < ${#modules[@]}; i++)) {
    module=${modules[i]}
    find "${build_dir}" -name "*.js" | xargs sed -i -e "s/require('${module}')/require('.\/${module}')/g"
}

コンパイルされたjavascriptのファイル中にある特定のrequirenode_modulesの中ではなくパスで解決する方法に変更します。require('kotlinx-serialization-runtime-js')require('./kotlinx-serialization-runtime-js')に書き換えるイメージ。

このファイルをビルドタスクの中で実行できるようにします。

ビルド定義ファイル

まずはfirebaes/functionsのpackage.jsonから

firebase/functions/puckage.json
{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "serve": "firebase serve --only functions",
    "shell": "firebase experimental:functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log",
    "replaceDepends": "./replace_depends.sh",
    "build": "../../gradlew :firebase:functions:assemble"
  },
  "dependencies": {
    "cors": "^2.8.4",
    "firebase-admin": "^5.4.3",
    "firebase-functions": "^0.7.1",
    "flickr-sdk": "^3.5.0"
  },
  "private": true
}

replaceDependsタスクを定義して、先程作ったスクリプトを呼び出せるようにしています。また、buildタスクを定義しておくことで、あとでfirebase.jsonの中からデプロイ前にビルドを行うよう設定できます。

次に、build.gradle

firebase/functions/build.gradle
buildscript {

    repositories {
        maven { url "https://plugins.gradle.org/m2/" }
        maven { url "http://dl.bintray.com/kotlin/kotlin-dev" }
        mavenCentral()
        maven { url "http://oss.jfrog.org" }
    }


    dependencies {
        classpath "com.moowork.gradle:gradle-node-plugin:1.2.0"
    }
}

apply plugin: 'kotlin-platform-js'
apply plugin: "kotlin-dce-js"
apply plugin: "com.moowork.node"
apply plugin: 'kotlinx-serialization'

node {
    download = true
}


repositories {
    mavenCentral()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
    compile project(":common-js")
}

compileKotlin2Js {
    kotlinOptions {
        sourceMap = true
        sourceMapEmbedSources = "always"
        moduleKind = 'commonjs'
        metaInfo = false
    }
}

kotlin {
    experimental {
        coroutines 'enable'
    }
}

task copyKotlinJs(type: Copy, dependsOn: compileKotlin2Js) {
    def workDir = "$buildDir/classes/kotlin/main/"
    from(workDir) {
        include "*.js"
        include "*.js.map"
    }
    into "$workDir/dependencies"
}

task devBuild(dependsOn: [npmInstall, copyKotlinJs])
afterEvaluate {
    copyKotlinJs.dependsOn copyDependenciesKotlinJs
}

task replaceDependsPath(type: NpmTask) {
    args = ["run", "replaceDepends"]
}

replaceDependsPath.dependsOn copyKotlinJs
assemble.finalizedBy replaceDependsPath

com.moowork.nodeプラグインを使ってnpmのタスクを呼び出すことができるようにしました。

copyKotlinJsを定義してビルドしたソースコードをdependencies以下にまとめるようにしています。これをやっておくとreplaceDependsPathタスクを実行するときのファイルの解決が楽。

replaceDependsPathnpm runを行うだけのタスクですが、ビルド終了時に実行できるようにassemble.finalizeByを設定しています。

最後に、firebase関連の設定があるfirebase.jsonです。

firebase/firebase.json
{
  "functions": {
    "predeploy": "npm --prefix functions run build"
  }
}

predeployでデプロイ前にビルドを実行します。

ソースコード

エントリーポイントとなるfirebase/functions/index.jsでは、ビルドしたファイルへの依存関係と関数の宣言を行います。

javascript
const functions = require('firebase-functions');
const myFunc = require("./build/classes/kotlin/main/dependencies/functions.js");

exports.hogeFunction = myFunc.hogeFunction

まとめ

ちょっと力技でしたが、kotlinで書かれたコードをCloud Funcitons for Firebase にデプロイできるようになりました。Cloud Functionsでprivate moduleが利用できない問題はAccess to private npm modules [36665861] - Visible to Public - Issue Trackerで議論されているようですので、今後進展があるかもしれません。

当面、この方法を利用してパワーで解決するのが良いのかなと思います💪💪

9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?