Software development of explosion! -夢の破片(カケラ)たちの日々-

ソフトウェア開発を中心としたコンピューター関連のネタを扱ったブログです

Software development is passion and explosion!

SpringBoot 2.3.x の 「OCI images using Cloud Native Buildpacks」を試してみた

背景

TwitterでSpringBoot2.3.xでは Cloud Native Buildpacks によって jib プラグインを使わずに DockerfileレスでDockerイメージをビルド出来ると教えていただきました。

な、なんだってーー!!

ということで、試さないかもと言っておきながら、早速試してみました。

環境

Java

  • Librca JDK 11.0.7
  • Spring Boot 2.3.1.RELEASE
  • Gradle 6.5
  • Jib Gradle Plugin 2.4.0 (比較用)
  • Docker Desktop for Mac 2.3.0.3 (45519)
    • docker-ce 19.03.8

ざっくりとしたまとめ

評価項目 Cloud Native Buildpacks Jib Gradle Plugin
手軽さ
スピード ×
機能性 ×
イメージサイズ
シンプルさ ×

詳細

手軽さ

jib と比べた場合、spring-boot-gradle-plugin(Maven の場合は spring-boot-maven-plugin)のみで完結するため、わざわざjib pluginのようなサードパーティプラグインを導入せずに使える。 とはいえ、jib plugin の導入自体も難しいものではないため、Dockerfileを書かずにコンテナー化するための敷居が下がった程度と言えよう。

スピード

きちんと時間計測はしていないが、jib pluginでビルドした方がビルド時間は短い。

機能性

Twitterでご指摘いただきました。とても有益な情報をありがとうございます!

Memory Calculator、Link-Local DNS、JVMKill Agent (個人的には特に前者2つ)はとても魅力的。 jib でやるには、事前にベースイメージとして、これらをインストールしたものを使うほか無いだろう。

生成するDockerイメージ名を変更出来る程度で、Dockerfileの FROM で指定するような内容は設定できそうに無い。(jibなら出来る) ベースイメージの変更は、おそらくbuilderのrun imageを差し替えることになる。 ただ、そうすると自前でbuilderを用意しないとならなさそう(Dockerfile3つとTOMLでいけるだろうけど)なので、jibよりはハードルが高そう。 (何となくだが、AWS CodeBuildのbuildspec.ymlとビルド環境として指定するイメージがひとまとめになったものがbuilderというイメージ。高機能故に少し複雑)

イメージのタイムスタンプをDockerの形式(?)に合わせることができなさそう(jibなら出来る)

Spring Boot Gradle Plugin Reference Guide

イメージサイズ

同じアプリをビルドして docker images で表示されるサイズを比較してみた。

Cloud Native Buildpacks でビルドした場合417MB。 jibでビルドした場合(jib.from.imageをデフォルトから変更しない場合です)366MB。

→ Memory Calculator、Link-Local DNS、JVMKill Agent が入っていること、layer数が多いことを考えると、左程の差ではなさそう。(ベースイメージのサイズが支配的になるお話。jibで mcr.microsoft.com/java/jdk:11-zulu-alpine を指定したら600MB超えたから充分、許容範囲内だろう。それでもデカいというならGraalVM使うしかない気がする)

シンプルさ

Cloud Native Buildpacks はとても高機能だが、その反面、jibほどシンプルでは無い(Builder/Stack/Run Imageが存在するので、それを理解しないとどのようにすればベースイメージを変えられるのか?がわからないなど)。 ということで、シンプルさはjibにあると思う。(シンプルが必ずしも良いとは言わないし、高機能も同じく。この辺はGo言語とRustのどちらの方がいいのか?という不毛な議論と同じようなお話だろう。手軽さ・使い勝手が大事なのだと思うんだけど…)

結論

現状では jib plugin を使用した方が ベースイメージ(DockerfileのFROMに指定するイメージ)を指定できたり、 タイムスタンプ形式の変更、外部ファイルの追加、 ビルドスピードといった面で優位である。 しかしながら、Memory Calculator、Link-Local DNS、JVMKill Agent といったミドルウェアの存在を考えると、Cloud Native Buildpacks の方が運用していく上で欲しいものが最初から付いてくるので良い。 また、毎回、[start.spring.io:title] で作ったプロジェクトに jib plugin を導入する手間を考えると、自動生成されたプロジェクトに設定済みのプラグインだけで完結するというのは嬉しい限り。 run image (ベースイメージ) を build.gradle で指定出来るようになってくれると嬉しいかも。(今はjibと比べるとちょっと敷居高い)

検証コード

jib

examples/java-example/morphological-analyis/kuromoji at 54e1698f705884a5a47a4c642a9cf67b7526f64d · poad/examples · GitHub

Cloud Native Buildpacks

Remove jib by poad · Pull Request #34 · poad/examples · GitHub

おまけ

ビルドコマンド (Gradle)

jib

./gradlew jibDockerBuild

Cloud Native Buildpacks

./gradlew bootBuildImage

AWS Amplify と AWS Cognito と TypeScript で作る ユーザー認証機能付き Next.js アプリ

AWS Amplify とは?

aws.amazon.com

モバイルアプリケーションやウェブアプリケーションを構築するための JavaScript フレームワークです。 これを使うことで、後述する AWS Cognito を簡単に使用できます。

AWS Cognito とは?

f:id:poad1010:20200222171716p:plain ユーザー管理を一括で行ってくれるユーザー認証サービスです。 Auth0 の AWS版といったところでしょうか。

f:id:poad1010:20200222171341p:plain f:id:poad1010:20200222171452p:plain f:id:poad1010:20200222171513p:plain

この様に色々と設定できます。

Next.js とは?

React.js でサーバーサイドレンダリングするためのフレームワークです。 Vue.js の Nuxt.js のようなものですね。

やってみよう!

1. Next.js with TypeScript な環境を作る

https://nextjs.org のサイトの手順を参考に、TypeScript に対応した Next.js アプリが実行できる環境を構築します。 ちなみに、筆者はyarn を使うことが多いですが、公式ドキュメントは npm ベースです。npm を使われる方は適宜、置き換えてお読みください。

$ mkdir next-amplify-example
[ken-yo@MacBook-Pro-2019-15inch ~]$ cd next-amplify-example 
[ken-yo@MacBook-Pro-2019-15inch next-amplify-example]$ yarn init -y
yarn init v1.22.0
warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications.
success Saved package.json
✨  Done in 0.02s.
[ken-yo@MacBook-Pro-2019-15inch next-amplify-example]$ yarn add --save react react-dom next
yarn add v1.22.0
info No lockfile found.
[1/4] 🔍  Resolving packages...
warning next > @babel/runtime-corejs2 > core-js@2.6.11: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.
warning next > styled-jsx > babel-types > babel-runtime > core-js@2.6.11: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning "next > styled-jsx@3.2.4" has unmet peer dependency "react@15.x.x || 16.x.x".
warning "next > use-subscription@1.1.1" has unmet peer dependency "react@^16.8.0".
warning " > react-dom@16.12.0" has unmet peer dependency "react@^16.0.0".
warning " > next@9.2.2" has unmet peer dependency "react@^16.6.0".
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 578 new dependencies.
info Direct dependencies
├─ next@9.2.2
└─ react-dom@16.12.0
info All dependencies
├─ @ampproject/toolbox-optimizer@2.0.0
├─ @ampproject/toolbox-runtime-version@2.0.0
├─ @ampproject/toolbox-script-csp@2.0.0
├─ @ampproject/toolbox-validator-rules@2.0.0
├─ @babel/core@7.7.2
├─ @babel/generator@7.8.4
├─ @babel/helper-builder-binary-assignment-operator-visitor@7.8.3
├─ @babel/helper-builder-react-jsx@7.8.3
├─ @babel/helper-call-delegate@7.8.3
├─ @babel/helper-create-class-features-plugin@7.8.3
├─ @babel/helper-define-map@7.8.3
├─ @babel/helper-explode-assignable-expression@7.8.3
├─ @babel/helper-wrap-function@7.8.3
├─ @babel/helpers@7.8.4
├─ @babel/highlight@7.8.3
├─ @babel/parser@7.8.4
├─ @babel/plugin-proposal-async-generator-functions@7.8.3
├─ @babel/plugin-proposal-class-properties@7.7.0
├─ @babel/plugin-proposal-dynamic-import@7.8.3
├─ @babel/plugin-proposal-json-strings@7.8.3
├─ @babel/plugin-proposal-nullish-coalescing-operator@7.7.4
├─ @babel/plugin-proposal-object-rest-spread@7.6.2
├─ @babel/plugin-proposal-optional-catch-binding@7.8.3
├─ @babel/plugin-proposal-optional-chaining@7.7.4
├─ @babel/plugin-proposal-unicode-property-regex@7.8.3
├─ @babel/plugin-syntax-async-generators@7.8.4
├─ @babel/plugin-syntax-bigint@7.8.3
├─ @babel/plugin-syntax-dynamic-import@7.8.3
├─ @babel/plugin-syntax-json-strings@7.8.3
├─ @babel/plugin-syntax-nullish-coalescing-operator@7.8.3
├─ @babel/plugin-syntax-object-rest-spread@7.8.3
├─ @babel/plugin-syntax-optional-catch-binding@7.8.3
├─ @babel/plugin-syntax-optional-chaining@7.8.3
├─ @babel/plugin-syntax-top-level-await@7.8.3
├─ @babel/plugin-syntax-typescript@7.8.3
├─ @babel/plugin-transform-arrow-functions@7.8.3
├─ @babel/plugin-transform-async-to-generator@7.8.3
├─ @babel/plugin-transform-block-scoped-functions@7.8.3
├─ @babel/plugin-transform-block-scoping@7.8.3
├─ @babel/plugin-transform-classes@7.8.3
├─ @babel/plugin-transform-computed-properties@7.8.3
├─ @babel/plugin-transform-destructuring@7.8.3
├─ @babel/plugin-transform-dotall-regex@7.8.3
├─ @babel/plugin-transform-duplicate-keys@7.8.3
├─ @babel/plugin-transform-exponentiation-operator@7.8.3
├─ @babel/plugin-transform-for-of@7.8.4
├─ @babel/plugin-transform-function-name@7.8.3
├─ @babel/plugin-transform-literals@7.8.3
├─ @babel/plugin-transform-member-expression-literals@7.8.3
├─ @babel/plugin-transform-modules-amd@7.8.3
├─ @babel/plugin-transform-modules-commonjs@7.7.0
├─ @babel/plugin-transform-modules-systemjs@7.8.3
├─ @babel/plugin-transform-modules-umd@7.8.3
├─ @babel/plugin-transform-named-capturing-groups-regex@7.8.3
├─ @babel/plugin-transform-new-target@7.8.3
├─ @babel/plugin-transform-object-super@7.8.3
├─ @babel/plugin-transform-parameters@7.8.4
├─ @babel/plugin-transform-property-literals@7.8.3
├─ @babel/plugin-transform-react-display-name@7.8.3
├─ @babel/plugin-transform-react-jsx-self@7.8.3
├─ @babel/plugin-transform-react-jsx-source@7.8.3
├─ @babel/plugin-transform-react-jsx@7.8.3
├─ @babel/plugin-transform-regenerator@7.8.3
├─ @babel/plugin-transform-reserved-words@7.8.3
├─ @babel/plugin-transform-runtime@7.6.2
├─ @babel/plugin-transform-shorthand-properties@7.8.3
├─ @babel/plugin-transform-spread@7.8.3
├─ @babel/plugin-transform-sticky-regex@7.8.3
├─ @babel/plugin-transform-template-literals@7.8.3
├─ @babel/plugin-transform-typeof-symbol@7.8.4
├─ @babel/plugin-transform-typescript@7.8.3
├─ @babel/plugin-transform-unicode-regex@7.8.3
├─ @babel/preset-env@7.7.1
├─ @babel/preset-modules@0.1.1
├─ @babel/preset-react@7.7.0
├─ @babel/preset-typescript@7.7.2
├─ @babel/runtime-corejs2@7.7.2
├─ @babel/runtime@7.7.2
├─ @next/polyfill-nomodule@9.2.2
├─ @webassemblyjs/floating-point-hex-parser@1.8.5
├─ @webassemblyjs/helper-code-frame@1.8.5
├─ @webassemblyjs/helper-fsm@1.8.5
├─ @webassemblyjs/helper-wasm-section@1.8.5
├─ @webassemblyjs/wasm-edit@1.8.5
├─ @webassemblyjs/wasm-opt@1.8.5
├─ @xtuc/ieee754@1.2.0
├─ abbrev@1.1.1
├─ accepts@1.3.7
├─ acorn@6.4.0
├─ adjust-sourcemap-loader@2.0.0
├─ ajv-errors@1.0.1
├─ ajv-keywords@3.4.1
├─ ajv@6.11.0
├─ amphtml-validator@1.0.23
├─ ansi-colors@3.2.4
├─ ansi-html@0.0.7
├─ ansi-styles@3.2.1
├─ anymatch@3.1.1
├─ aproba@1.2.0
├─ are-we-there-yet@1.1.5
├─ argparse@1.0.10
├─ arity-n@1.0.4
├─ arr-flatten@1.1.0
├─ array-union@1.0.2
├─ array-uniq@1.0.3
├─ asap@2.0.6
├─ asn1.js@4.10.1
├─ assert@1.4.1
├─ assign-symbols@1.0.0
├─ ast-types@0.13.2
├─ async-each@1.0.3
├─ async-retry@1.2.3
├─ async-sema@3.0.0
├─ atob@2.1.2
├─ autodll-webpack-plugin@0.4.2
├─ autoprefixer@9.7.4
├─ babel-code-frame@6.26.0
├─ babel-core@7.0.0-bridge.0
├─ babel-loader@8.0.6
├─ babel-plugin-transform-define@2.0.0
├─ babel-plugin-transform-react-remove-prop-types@0.4.24
├─ babel-runtime@6.26.0
├─ babel-types@6.26.0
├─ base@0.11.2
├─ base64-js@1.3.1
├─ binary-extensions@2.0.0
├─ bindings@1.5.0
├─ bluebird@3.7.2
├─ brace-expansion@1.1.11
├─ braces@2.3.2
├─ browserify-aes@1.2.0
├─ browserify-cipher@1.0.1
├─ browserify-des@1.0.2
├─ browserify-sign@4.0.4
├─ browserify-zlib@0.2.0
├─ buffer-equal-constant-time@1.0.1
├─ buffer-json@2.0.0
├─ buffer-xor@1.0.3
├─ buffer@4.9.2
├─ builtin-status-codes@3.0.0
├─ bytes@3.0.0
├─ cacache@12.0.3
├─ cache-base@1.0.1
├─ cache-loader@4.1.0
├─ caller-callsite@2.0.0
├─ caller-path@2.0.0
├─ callsites@2.0.0
├─ camelcase@5.3.1
├─ caniuse-lite@1.0.30001028
├─ chalk@2.4.2
├─ chokidar@3.3.1
├─ chownr@1.1.4
├─ chrome-trace-event@1.0.2
├─ ci-info@2.0.0
├─ class-utils@0.3.6
├─ cli-cursor@2.1.0
├─ cli-spinners@2.2.0
├─ clone-deep@4.0.1
├─ clone@1.0.4
├─ code-point-at@1.1.0
├─ collection-visit@1.0.0
├─ color-convert@1.9.3
├─ color-name@1.1.3
├─ colors@1.1.2
├─ compose-function@3.0.3
├─ compressible@2.0.18
├─ compression@1.7.4
├─ concat-map@0.0.1
├─ concat-stream@1.6.2
├─ conf@5.0.0
├─ console-browserify@1.2.0
├─ console-control-strings@1.1.0
├─ constants-browserify@1.0.0
├─ content-type@1.0.4
├─ cookie@0.4.0
├─ copy-concurrently@1.0.5
├─ copy-descriptor@0.1.1
├─ core-js-compat@3.6.4
├─ core-js@2.6.11
├─ core-util-is@1.0.2
├─ cosmiconfig@5.2.1
├─ create-ecdh@4.0.3
├─ create-hmac@1.1.7
├─ crypto-browserify@3.12.0
├─ css-blank-pseudo@0.1.4
├─ css-has-pseudo@0.10.0
├─ css-loader@3.3.0
├─ css-prefers-color-scheme@3.1.1
├─ css@2.2.4
├─ cssdb@4.4.0
├─ cssnano-preset-simple@1.0.3
├─ cssnano-simple@1.0.0
├─ cyclist@1.0.1
├─ d@1.0.1
├─ debug@2.6.9
├─ decode-uri-component@0.2.0
├─ deep-extend@0.6.0
├─ defaults@1.0.3
├─ define-properties@1.1.3
├─ del@3.0.0
├─ delegates@1.0.0
├─ des.js@1.0.1
├─ destroy@1.0.4
├─ detect-libc@1.0.3
├─ devalue@2.0.1
├─ diffie-hellman@5.0.3
├─ dom-serializer@0.2.2
├─ domain-browser@1.2.0
├─ domutils@2.0.0
├─ dot-prop@5.2.0
├─ duplexer@0.1.1
├─ duplexify@3.7.1
├─ ecdsa-sig-formatter@1.0.11
├─ ee-first@1.1.1
├─ electron-to-chromium@1.3.358
├─ emojis-list@3.0.0
├─ encodeurl@1.0.2
├─ enhanced-resolve@4.1.1
├─ env-paths@2.2.0
├─ errno@0.1.7
├─ error-ex@1.3.2
├─ es5-ext@0.10.53
├─ es6-iterator@2.0.3
├─ es6-symbol@3.1.3
├─ escape-html@1.0.3
├─ escape-string-regexp@1.0.5
├─ eslint-scope@4.0.3
├─ esprima@4.0.1
├─ esrecurse@4.2.1
├─ estraverse@4.3.0
├─ esutils@2.0.3
├─ etag@1.8.1
├─ eventemitter3@4.0.0
├─ events@3.1.0
├─ evp_bytestokey@1.0.3
├─ expand-brackets@2.1.4
├─ ext@1.4.0
├─ extglob@2.0.4
├─ fast-deep-equal@3.1.1
├─ fast-json-stable-stringify@2.1.0
├─ file-loader@4.2.0
├─ file-uri-to-path@1.0.0
├─ fill-range@4.0.0
├─ finally-polyfill@0.1.0
├─ find-cache-dir@2.1.0
├─ flatten@1.0.3
├─ flush-write-stream@1.1.1
├─ follow-redirects@1.10.0
├─ for-in@1.0.2
├─ fork-ts-checker-webpack-plugin@3.1.1
├─ from2@2.3.0
├─ fs-minipass@1.2.7
├─ fs.realpath@1.0.0
├─ fsevents@2.1.2
├─ gauge@2.7.4
├─ get-value@2.0.6
├─ glob-parent@5.1.0
├─ glob-to-regexp@0.4.1
├─ glob@7.1.6
├─ globby@6.1.0
├─ graceful-readlink@1.0.1
├─ gzip-size@5.1.1
├─ has-ansi@2.0.0
├─ has-symbols@1.0.1
├─ has-unicode@2.0.1
├─ has-value@1.0.0
├─ has@1.0.3
├─ hash.js@1.1.7
├─ hmac-drbg@1.0.1
├─ hosted-git-info@2.8.5
├─ html-entities@1.2.1
├─ htmlparser2@4.0.0
├─ http-errors@1.7.2
├─ http-proxy@1.18.0
├─ https-browserify@1.0.0
├─ iconv-lite@0.4.24
├─ icss-utils@4.1.1
├─ ieee754@1.1.13
├─ ignore-loader@0.1.2
├─ ignore-walk@3.0.3
├─ import-cwd@2.1.0
├─ import-fresh@2.0.0
├─ import-from@2.1.0
├─ infer-owner@1.0.4
├─ inflight@1.0.6
├─ ini@1.3.5
├─ invariant@2.2.4
├─ is-accessor-descriptor@1.0.0
├─ is-arrayish@0.2.1
├─ is-binary-path@2.1.0
├─ is-data-descriptor@1.0.0
├─ is-descriptor@1.0.2
├─ is-directory@0.3.1
├─ is-docker@2.0.0
├─ is-extglob@2.1.1
├─ is-fullwidth-code-point@1.0.0
├─ is-glob@4.0.1
├─ is-obj@2.0.0
├─ is-path-cwd@1.0.0
├─ is-path-in-cwd@1.0.1
├─ is-path-inside@1.0.1
├─ is-plain-obj@1.1.0
├─ is-plain-object@2.0.4
├─ is-windows@1.0.2
├─ is-wsl@2.1.1
├─ isarray@1.0.0
├─ jest-worker@24.9.0
├─ js-levenshtein@1.1.6
├─ js-tokens@4.0.0
├─ js-yaml@3.13.1
├─ jsesc@2.5.2
├─ json-parse-better-errors@1.0.2
├─ json-schema-traverse@0.4.1
├─ json-schema-typed@7.0.3
├─ jsonwebtoken@8.5.1
├─ jwa@1.4.1
├─ jws@3.2.2
├─ launch-editor@2.2.1
├─ load-json-file@2.0.0
├─ loader-runner@2.4.0
├─ lodash.curry@4.1.1
├─ lodash.includes@4.3.0
├─ lodash.isboolean@3.0.3
├─ lodash.isinteger@4.0.4
├─ lodash.isnumber@3.0.3
├─ lodash.isplainobject@4.0.6
├─ lodash.isstring@4.0.1
├─ lodash.once@4.1.1
├─ lodash.template@4.5.0
├─ lodash.templatesettings@4.2.0
├─ log-symbols@2.2.0
├─ loose-envify@1.4.0
├─ lru-cache@5.1.1
├─ mamacro@0.0.3
├─ map-visit@1.0.0
├─ merge-stream@2.0.0
├─ microevent.ts@0.1.1
├─ miller-rabin@4.0.1
├─ mime-db@1.43.0
├─ mime-types@2.1.26
├─ mime@1.6.0
├─ mimic-fn@1.2.0
├─ mini-css-extract-plugin@0.8.0
├─ minimalistic-crypto-utils@1.0.1
├─ minimatch@3.0.4
├─ minimist@1.2.0
├─ minipass@2.9.0
├─ minizlib@1.3.3
├─ mississippi@3.0.0
├─ mixin-deep@1.3.2
├─ move-concurrently@1.0.1
├─ nan@2.14.0
├─ nanomatch@1.2.13
├─ needle@2.3.2
├─ negotiator@0.6.2
├─ next-tick@1.0.0
├─ next@9.2.2
├─ node-libs-browser@2.2.1
├─ node-pre-gyp@0.14.0
├─ node-releases@1.1.50
├─ nopt@4.0.1
├─ normalize-html-whitespace@1.0.0
├─ normalize-package-data@2.5.0
├─ normalize-range@0.1.2
├─ normalize-url@1.9.1
├─ npm-bundled@1.1.1
├─ npm-packlist@1.4.8
├─ npmlog@4.1.2
├─ num2fraction@1.2.2
├─ number-is-nan@1.0.1
├─ object-assign@4.1.1
├─ object-copy@0.1.0
├─ object-keys@1.1.1
├─ object-path@0.11.4
├─ on-finished@2.3.0
├─ on-headers@1.0.2
├─ onetime@2.0.1
├─ ora@3.4.0
├─ os-browserify@0.3.0
├─ os-homedir@1.0.2
├─ os-tmpdir@1.0.2
├─ osenv@0.1.5
├─ p-limit@2.2.2
├─ p-locate@4.1.0
├─ p-map@1.2.0
├─ p-try@2.2.0
├─ pako@1.0.11
├─ parallel-transform@1.2.0
├─ parse-json@2.2.0
├─ pascalcase@0.1.1
├─ path-browserify@0.0.1
├─ path-dirname@1.0.2
├─ path-is-inside@1.0.2
├─ path-parse@1.0.6
├─ path-to-regexp@6.1.0
├─ path-type@2.0.0
├─ picomatch@2.2.1
├─ pinkie-promise@2.0.1
├─ pinkie@2.0.4
├─ pkg-dir@3.0.0
├─ pkg-up@3.1.0
├─ pnp-webpack-plugin@1.5.0
├─ posix-character-classes@0.1.1
├─ postcss-attribute-case-insensitive@4.0.2
├─ postcss-color-functional-notation@2.0.1
├─ postcss-color-gray@5.0.0
├─ postcss-color-hex-alpha@5.0.3
├─ postcss-color-mod-function@3.0.3
├─ postcss-color-rebeccapurple@4.0.1
├─ postcss-custom-media@7.0.8
├─ postcss-custom-properties@8.0.11
├─ postcss-custom-selectors@5.1.2
├─ postcss-dir-pseudo-class@5.0.0
├─ postcss-double-position-gradients@1.0.0
├─ postcss-env-function@2.0.2
├─ postcss-flexbugs-fixes@4.1.0
├─ postcss-focus-visible@4.0.0
├─ postcss-focus-within@3.0.0
├─ postcss-font-variant@4.0.0
├─ postcss-gap-properties@2.0.0
├─ postcss-image-set-function@3.0.1
├─ postcss-initial@3.0.2
├─ postcss-lab-function@2.0.1
├─ postcss-load-config@2.1.0
├─ postcss-loader@3.0.0
├─ postcss-logical@3.0.0
├─ postcss-media-minmax@4.0.0
├─ postcss-modules-extract-imports@2.0.0
├─ postcss-modules-local-by-default@3.0.2
├─ postcss-modules-scope@2.1.1
├─ postcss-modules-values@3.0.0
├─ postcss-nesting@7.0.1
├─ postcss-overflow-shorthand@2.0.0
├─ postcss-page-break@2.0.0
├─ postcss-place@4.0.1
├─ postcss-preset-env@6.7.0
├─ postcss-pseudo-class-any-link@6.0.0
├─ postcss-replace-overflow-wrap@3.0.0
├─ postcss-selector-matches@4.0.0
├─ postcss-selector-not@4.0.0
├─ prepend-http@1.0.4
├─ private@0.1.8
├─ process-nextick-args@2.0.1
├─ process@0.11.10
├─ promise-inflight@1.0.1
├─ promise@7.1.1
├─ prop-types-exact@1.2.0
├─ prop-types@15.7.2
├─ prr@1.0.1
├─ public-encrypt@4.0.3
├─ pump@3.0.0
├─ pumpify@1.5.1
├─ punycode@1.3.2
├─ query-string@4.3.4
├─ querystring-es3@0.2.1
├─ querystring@0.2.0
├─ randomfill@1.0.4
├─ range-parser@1.2.1
├─ raw-body@2.4.0
├─ rc@1.2.8
├─ react-dom@16.12.0
├─ react-error-overlay@5.1.6
├─ react-is@16.8.6
├─ read-pkg@2.0.0
├─ readable-stream@2.3.7
├─ readdirp@3.3.0
├─ recast@0.18.5
├─ reflect.ownkeys@0.2.0
├─ regenerate-unicode-properties@8.1.0
├─ regenerator-transform@0.14.1
├─ regex-parser@2.2.10
├─ regexpu-core@4.6.0
├─ regjsgen@0.5.1
├─ regjsparser@0.6.3
├─ remove-trailing-separator@1.1.0
├─ repeat-element@1.1.3
├─ requires-port@1.0.0
├─ resolve-url-loader@3.1.1
├─ resolve-url@0.2.1
├─ resolve@1.15.1
├─ restore-cursor@2.0.0
├─ ret@0.1.15
├─ retry@0.12.0
├─ rework-visit@1.0.0
├─ rework@1.0.1
├─ run-queue@1.0.3
├─ safer-buffer@2.1.2
├─ sass-loader@8.0.2
├─ sax@1.2.4
├─ scheduler@0.18.0
├─ schema-utils@2.6.4
├─ semver@5.7.1
├─ send@0.17.1
├─ serialize-javascript@2.1.2
├─ set-blocking@2.0.0
├─ set-value@2.0.1
├─ setimmediate@1.0.5
├─ shallow-clone@3.0.1
├─ shell-quote@1.7.2
├─ signal-exit@3.0.2
├─ snapdragon-node@2.1.1
├─ snapdragon-util@3.0.1
├─ sort-keys@1.1.2
├─ source-list-map@2.0.1
├─ source-map-resolve@0.5.3
├─ source-map-url@0.4.0
├─ spdx-correct@3.1.0
├─ spdx-exceptions@2.2.0
├─ split-string@3.1.0
├─ sprintf-js@1.0.3
├─ ssri@6.0.1
├─ static-extend@0.1.2
├─ stream-browserify@2.0.2
├─ stream-each@1.2.3
├─ stream-http@2.8.3
├─ strict-uri-encode@1.1.0
├─ string_decoder@1.1.1
├─ string-width@1.0.2
├─ strip-ansi@3.0.1
├─ strip-bom@3.0.0
├─ strip-json-comments@2.0.1
├─ style-loader@1.0.0
├─ styled-jsx@3.2.4
├─ stylis-rule-sheet@0.0.10
├─ stylis@3.5.4
├─ tar@4.4.13
├─ terser-webpack-plugin@1.4.3
├─ terser@4.6.3
├─ thread-loader@2.1.3
├─ through2@2.0.5
├─ timers-browserify@2.0.11
├─ to-arraybuffer@1.0.1
├─ to-fast-properties@1.0.3
├─ to-object-path@0.3.0
├─ to-regex-range@2.1.1
├─ traverse@0.6.6
├─ ts-pnp@1.1.6
├─ tslib@1.11.0
├─ tty-browserify@0.0.0
├─ type@1.2.0
├─ typedarray-to-buffer@3.1.5
├─ typedarray@0.0.6
├─ unfetch@4.1.0
├─ unicode-canonical-property-names-ecmascript@1.0.4
├─ unicode-match-property-ecmascript@1.0.4
├─ unicode-match-property-value-ecmascript@1.1.0
├─ unicode-property-aliases-ecmascript@1.0.5
├─ union-value@1.0.1
├─ unique-filename@1.1.1
├─ unique-slug@2.0.2
├─ unpipe@1.0.0
├─ unset-value@1.0.0
├─ upath@1.2.0
├─ uri-js@4.2.2
├─ url-polyfill@1.1.7
├─ url@0.11.0
├─ use-subscription@1.1.1
├─ use@3.1.1
├─ util-deprecate@1.0.2
├─ uuid@3.4.0
├─ validate-npm-package-license@3.0.4
├─ vary@1.1.2
├─ vm-browserify@1.1.2
├─ watchpack@2.0.0-beta.5
├─ wcwidth@1.0.1
├─ webpack-dev-middleware@3.7.0
├─ webpack-hot-middleware@2.25.0
├─ webpack-log@2.0.0
├─ webpack-merge@4.2.2
├─ webpack-sources@1.4.3
├─ webpack@4.41.2
├─ wide-align@1.1.3
├─ worker-farm@1.7.0
├─ worker-rpc@0.1.1
├─ write-file-atomic@3.0.1
├─ xtend@4.0.2
├─ y18n@4.0.0
└─ yallist@3.1.1
✨  Done in 10.63s.

$ mkdir -p src/pages
$ yarn add --save-dev typescript @types/react @types/node
yarn add v1.22.0
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning " > next@9.2.2" has unmet peer dependency "react@^16.6.0".
warning "next > styled-jsx@3.2.4" has unmet peer dependency "react@15.x.x || 16.x.x".
warning "next > use-subscription@1.1.1" has unmet peer dependency "react@^16.8.0".
warning " > react-dom@16.12.0" has unmet peer dependency "react@^16.0.0".
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 4 new dependencies.
info Direct dependencies
├─ @types/node@13.7.4
└─ @types/react@16.9.22
info All dependencies
├─ @types/node@13.7.4
├─ @types/prop-types@15.7.3
├─ @types/react@16.9.22
└─ csstype@2.6.9
✨  Done in 2.02s.

$ yarn add react       
yarn add v1.22.0
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ react@16.12.0
info All dependencies
└─ react@16.12.0
✨  Done in 1.73s.

$ yarn run dev
yarn run v1.22.0
$ next
[ wait ]  starting the development server ...
[ info ]  waiting on http://localhost:3000 ...
It looks like you're trying to use TypeScript but do not have the required package(s) installed.

Please install typescript by running:

    yarn add --dev typescript

If you are not trying to use TypeScript, please remove the tsconfig.json file from your package root (and any TypeScript files).

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

$ yarn add --dev typescript
yarn add v1.22.0
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ typescript@3.8.2
info All dependencies
└─ typescript@3.8.2
✨  Done in 2.02s.

tsconfig.json や、 next.config.js 、public/sw.js などは各自調べて追加してください。(ぉぃ

f:id:poad1010:20200222201750p:plain

地味に大変なので、テンプレートリポジトリー化しています。

github.com

2. amplify の導入

オフィシャルドキュメント https://aws-amplify.github.io/docs/ のとおり、CLIインストールと設定を行います。

npm install -g @aws-amplify/cli
amplify configure

AWS 管理コンソールが起動して、使うリージョンだとか、amplify を管理するための(?) IAM ユーザーの作成などをターミナルに表示されたメッセージに従って行います。

3. アプリケーションへの適用

3.1. nom モジュールのインストール

amplify には色々な AWS サービスを使うための npm モジュールがあるようですが、今回は以下の 3つを使用します。

  • aws-amplify
  • @aws-amplify/auth
  • aws-amplify-react
yarn add aws-amplify @aws-amplify/auth aws-amplify-react

3.2. amplify アプリの初期化

3.2.1. amplify init
amplify init

を実行すると、プロジェクト名や使用するエディター、環境名などの選択や入力を求められます。 指示に従って進めると、バックエンドコードが出力されます。

これは、後ほど amplify コマンドで使うことになる CloudFormation テンプレートや CloudFormation Stack などです。

$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project next-amplify-example
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path:  src
? Distribution Directory Path: build
? Build Command:  yarn build
? Start Command: yarn start
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use amplify
Adding backend environment dev to AWS Amplify Console app: d1ntmmvoqyxue2
⠏ Initializing project in the cloud...

CREATE_IN_PROGRESS DeploymentBucket                        AWS::S3::Bucket            Sat Feb 22 2020 19:27:48 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UnauthRole                              AWS::IAM::Role             Sat Feb 22 2020 19:27:47 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS AuthRole                                AWS::IAM::Role             Sat Feb 22 2020 19:27:47 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UnauthRole                              AWS::IAM::Role             Sat Feb 22 2020 19:27:46 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS DeploymentBucket                        AWS::S3::Bucket            Sat Feb 22 2020 19:27:46 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS AuthRole                                AWS::IAM::Role             Sat Feb 22 2020 19:27:46 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS amplify-next-amplify-example-dev-192739 AWS::CloudFormation::Stack Sat Feb 22 2020 19:27:43 GMT+0900 (日本標準時) User Initiated             
⠼ Initializing project in the cloud...

CREATE_COMPLETE UnauthRole AWS::IAM::Role Sat Feb 22 2020 19:28:01 GMT+0900 (日本標準時) 
CREATE_COMPLETE AuthRole   AWS::IAM::Role Sat Feb 22 2020 19:28:01 GMT+0900 (日本標準時) 
⠼ Initializing project in the cloud...

CREATE_COMPLETE DeploymentBucket AWS::S3::Bucket Sat Feb 22 2020 19:28:08 GMT+0900 (日本標準時) 
⠦ Initializing project in the cloud...

CREATE_COMPLETE amplify-next-amplify-example-dev-192739 AWS::CloudFormation::Stack Sat Feb 22 2020 19:28:11 GMT+0900 (日本標準時) 
✔ Successfully created initial AWS cloud resources for deployments.
✔ Initialized provider successfully.
Initialized your environment successfully.

Your project has been successfully initialized and connected to the cloud!

Some next steps:
"amplify status" will show you what you've added already and if it's locally configured or deployed
"amplify add <category>" will allow you to add features like user login or a backend API
"amplify push" will build all your local backend resources and provision it in the cloud
“amplify console” to open the Amplify Console and view your project status
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

Pro tip:
Try "amplify add api" to create a backend API and then "amplify publish" to deploy everything
import React from 'react'

class Home extends React.Component {
  render() {
    return (
      <div>

        <div className="hero">
          <h1 className="title">Welcome to Next.js!</h1>
          <p className="description">
            To get started, edit <code>pages/index.js</code> and save to reload.
          </p>

          <div className="row">
            <a href="https://nextjs.org/docs" className="card">
              <h3>Documentation &rarr;</h3>
              <p>Learn more about Next.js in the documentation.</p>
            </a>
            <a href="https://nextjs.org/learn" className="card">
              <h3>Next.js Learn &rarr;</h3>
              <p>Learn about Next.js by following an interactive tutorial!</p>
            </a>
            <a
              href="https://github.com/zeit/next.js/tree/master/examples"
              className="card"
            >
              <h3>Examples &rarr;</h3>
              <p>Find other example boilerplates on the Next.js GitHub.</p>
            </a>
          </div>
        </div>

        <style jsx>{`
          .hero {
            width: 100%;
            color: #333;
          }
          .title {
            margin: 0;
            width: 100%;
            padding-top: 80px;
            line-height: 1.15;
            font-size: 48px;
          }
          .title,
          .description {
            text-align: center;
          }
          .row {
            max-width: 880px;
            margin: 80px auto 40px;
            display: flex;
            flex-direction: row;
            justify-content: space-around;
          }
          .card {
            padding: 18px 18px 24px;
            width: 220px;
            text-align: left;
            text-decoration: none;
            color: #434343;
            border: 1px solid #9b9b9b;
          }
          .card:hover {
            border-color: #067df7;
          }
          .card h3 {
            margin: 0;
            color: #067df7;
            font-size: 18px;
          }
          .card p {
            margin: 0;
            padding: 12px 0 0;
            font-size: 13px;
            color: #333;
          }
        `}</style>
      </div>
    )
  }
}

export default Home

f:id:poad1010:20200222190051p:plain

3.2.2. amplify add auth

amplify から Cognito を使用して認証するため、 amplify add auth を行います。 多分、Cognito 以外も使えそうですが、今回は Cognito を使うために追加するとお考えください。

amplify add auth

こんな感じです。今回はザルセキュリティで良い(あくまでもサンプルなので)ので、ユーザー名とパスワードでのみの認証とします。 (本来なら、メールアドレス他、色々と設定すべきでしょう)

$ amplify add auth
Using service: Cognito, provided by: awscloudformation
 
 The current configured provider is Amazon Cognito. 
 
 Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections. 
 How do you want users to be able to sign in? Username
 Do you want to configure advanced settings? No, I am done.
Successfully added resource nextamplifyexample6c8f8585 locally

Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

3.3. amplify バックグラウンド(AWS側のリソース)の構築

"amplify push" will build all your local backend resources and provision it in the cloud とあるように、AWS側のリソースを構築する必要があります。

amplify push

を実行すると、CloudFormation スタックが更新されて ネステッドスタックとなります。 また、Cognito のリソースが作られます。

$ amplify push    
✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name              | Operation | Provider plugin   |
| -------- | -------------------------- | --------- | ----------------- |
| Auth     | nextamplifyexample0dde88af | Create    | awscloudformation |
? Are you sure you want to continue? Yes
⠏ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS authnextamplifyexample0dde88af          AWS::CloudFormation::Stack Sat Feb 22 2020 19:37:35 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS authnextamplifyexample0dde88af          AWS::CloudFormation::Stack Sat Feb 22 2020 19:37:34 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionRole          AWS::IAM::Role             Sat Feb 22 2020 19:37:34 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionRole          AWS::IAM::Role             Sat Feb 22 2020 19:37:33 GMT+0900 (日本標準時)                            
UPDATE_IN_PROGRESS amplify-next-amplify-example-dev-192739 AWS::CloudFormation::Stack Sat Feb 22 2020 19:37:29 GMT+0900 (日本標準時) User Initiated             
⠴ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS amplify-next-amplify-example-dev-192739-authnextamplifyexample0dde88af-1PWQ9Y8ZW1F9F AWS::CloudFormation::Stack Sat Feb 22 2020 19:37:35 GMT+0900 (日本標準時) User Initiated
⠇ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS SNSRole AWS::IAM::Role Sat Feb 22 2020 19:37:40 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS SNSRole AWS::IAM::Role Sat Feb 22 2020 19:37:40 GMT+0900 (日本標準時)                            
⠴ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE UpdateRolesWithIDPFunctionRole AWS::IAM::Role Sat Feb 22 2020 19:37:48 GMT+0900 (日本標準時) 
⠹ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE SNSRole AWS::IAM::Role Sat Feb 22 2020 19:37:55 GMT+0900 (日本標準時) 
⠼ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPool AWS::Cognito::UserPool Sat Feb 22 2020 19:37:58 GMT+0900 (日本標準時) 
⠸ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    UserPool AWS::Cognito::UserPool Sat Feb 22 2020 19:38:01 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS UserPool AWS::Cognito::UserPool Sat Feb 22 2020 19:38:01 GMT+0900 (日本標準時) Resource creation Initiated
⠴ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    UserPoolClient    AWS::Cognito::UserPoolClient Sat Feb 22 2020 19:38:06 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS UserPoolClient    AWS::Cognito::UserPoolClient Sat Feb 22 2020 19:38:05 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_COMPLETE    UserPoolClientWeb AWS::Cognito::UserPoolClient Sat Feb 22 2020 19:38:05 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS UserPoolClientWeb AWS::Cognito::UserPoolClient Sat Feb 22 2020 19:38:05 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClient    AWS::Cognito::UserPoolClient Sat Feb 22 2020 19:38:04 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS UserPoolClientWeb AWS::Cognito::UserPoolClient Sat Feb 22 2020 19:38:04 GMT+0900 (日本標準時)                            
⠇ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientRole AWS::IAM::Role Sat Feb 22 2020 19:38:10 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientRole AWS::IAM::Role Sat Feb 22 2020 19:38:09 GMT+0900 (日本標準時)                            
⠸ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE UserPoolClientRole AWS::IAM::Role Sat Feb 22 2020 19:38:24 GMT+0900 (日本標準時) 
⠸ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    UserPoolClientLambda AWS::Lambda::Function Sat Feb 22 2020 19:38:28 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS UserPoolClientLambda AWS::Lambda::Function Sat Feb 22 2020 19:38:27 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientLambda AWS::Lambda::Function Sat Feb 22 2020 19:38:27 GMT+0900 (日本標準時)                            
⠹ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientLambdaPolicy AWS::IAM::Policy Sat Feb 22 2020 19:38:31 GMT+0900 (日本標準時) 
⠼ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientLambdaPolicy AWS::IAM::Policy Sat Feb 22 2020 19:38:32 GMT+0900 (日本標準時) Resource creation Initiated
⠇ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE UserPoolClientLambdaPolicy AWS::IAM::Policy Sat Feb 22 2020 19:38:45 GMT+0900 (日本標準時) 
⠧ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientLogPolicy AWS::IAM::Policy Sat Feb 22 2020 19:38:49 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UserPoolClientLogPolicy AWS::IAM::Policy Sat Feb 22 2020 19:38:48 GMT+0900 (日本標準時)                            
⠹ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UserPoolClientInputs    Custom::LambdaCallout Sat Feb 22 2020 19:39:05 GMT+0900 (日本標準時) 
CREATE_COMPLETE    UserPoolClientLogPolicy AWS::IAM::Policy      Sat Feb 22 2020 19:39:03 GMT+0900 (日本標準時) 
⠹ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    UserPoolClientInputs Custom::LambdaCallout Sat Feb 22 2020 19:39:09 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS UserPoolClientInputs Custom::LambdaCallout Sat Feb 22 2020 19:39:09 GMT+0900 (日本標準時) Resource creation Initiated
⠦ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    IdentityPool AWS::Cognito::IdentityPool Sat Feb 22 2020 19:39:14 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS IdentityPool AWS::Cognito::IdentityPool Sat Feb 22 2020 19:39:13 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS IdentityPool AWS::Cognito::IdentityPool Sat Feb 22 2020 19:39:12 GMT+0900 (日本標準時)                            
⠦ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE    IdentityPoolRoleMap AWS::Cognito::IdentityPoolRoleAttachment Sat Feb 22 2020 19:39:20 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS IdentityPoolRoleMap AWS::Cognito::IdentityPoolRoleAttachment Sat Feb 22 2020 19:39:20 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS IdentityPoolRoleMap AWS::Cognito::IdentityPoolRoleAttachment Sat Feb 22 2020 19:39:18 GMT+0900 (日本標準時)                            
⠙ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE amplify-next-amplify-example-dev-192739-authnextamplifyexample0dde88af-1PWQ9Y8ZW1F9F AWS::CloudFormation::Stack Sat Feb 22 2020 19:39:22 GMT+0900 (日本標準時) 
⠋ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE authnextamplifyexample0dde88af AWS::CloudFormation::Stack Sat Feb 22 2020 19:39:29 GMT+0900 (日本標準時) 
⠏ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UpdateRolesWithIDPFunction AWS::Lambda::Function Sat Feb 22 2020 19:39:31 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS UpdateRolesWithIDPFunction AWS::Lambda::Function Sat Feb 22 2020 19:39:30 GMT+0900 (日本標準時)                            
⠹ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS UpdateRolesWithIDPFunctionOutputs Custom::LambdaCallout Sat Feb 22 2020 19:39:33 GMT+0900 (日本標準時) 
CREATE_COMPLETE    UpdateRolesWithIDPFunction        AWS::Lambda::Function Sat Feb 22 2020 19:39:31 GMT+0900 (日本標準時) 
⠼ Updating resources in the cloud. This may take a few minutes...

UPDATE_COMPLETE                     amplify-next-amplify-example-dev-192739 AWS::CloudFormation::Stack Sat Feb 22 2020 19:39:39 GMT+0900 (日本標準時)                            
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS amplify-next-amplify-example-dev-192739 AWS::CloudFormation::Stack Sat Feb 22 2020 19:39:39 GMT+0900 (日本標準時)                            
CREATE_COMPLETE                     UpdateRolesWithIDPFunctionOutputs       Custom::LambdaCallout      Sat Feb 22 2020 19:39:37 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS                  UpdateRolesWithIDPFunctionOutputs       Custom::LambdaCallout      Sat Feb 22 2020 19:39:36 GMT+0900 (日本標準時) Resource creation Initiated
✔ All resources are updated in the cloud

ここまでで、Cognito を呼び出す下地は出来ました。

あとは、アプリのコードから amplify を使って Cognito を呼び出すだけです!

3.4. アプリへの組み込み

React & React Native

に従って組み込んでみましょう!

因みに、今回は pages/index.tsx へ組み込みました。 Layout など、components 配下のコンポーネントを必ず組み込むようにする場合は、そちらに記述した方が良いかもしれません。

import Amplify from 'aws-amplify';
import config from '../aws-exports';

Amplify.configure(config);

class Home extends React.Component {

  render() {
    return (
      <div>

// 以下略

続いて pages/_app.tsx

import * as React from 'react';
import App from 'next/app';
import { withAuthenticator } from 'aws-amplify-react';

class MyApp extends App {
    render() {
        const { Component, pageProps } = this.props;
        return (<Component {...pageProps} />)
    }
}

export default withAuthenticator(MyApp)

さぁ!これで yarn run dev すっぞ!

$ yarn run dev
yarn run v1.22.0
$ next
[ wait ]  starting the development server ...
[ info ]  waiting on http://localhost:3000 ...
[ error ] ./node_modules/@aws-amplify/ui/dist/style.css
Global CSS cannot be imported from within node_modules.
Read more: https://err.sh/next.js/css-npm
Location: node_modules/aws-amplify-react/lib-esm/Amplify-UI/Amplify-UI-Components-React.js

はい。。。コケました。

Next.js project fails to build, Unexpected token : at dist/style.css · Issue #3854 · aws-amplify/amplify-js · GitHub が原因のようですね。

@zeit/next-css を追加して、 next.config.js を修正すれば良いようです。

$ yarn add -D @zeit/next-css
yarn add v1.22.0
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning "aws-amplify > @aws-amplify/api@2.1.5" has incorrect peer dependency "@aws-amplify/pubsub@^1.2.4".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/analytics@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/api@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/auth@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/core@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/interactions@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/storage@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/ui@^2.0.0".
warning "@zeit/next-css > css-loader@1.0.0" has unmet peer dependency "webpack@^4.0.0".
warning "@zeit/next-css > mini-css-extract-plugin@0.4.3" has unmet peer dependency "webpack@^4.4.0".
[4/4] 🔨  Building fresh packages...
warning "@zeit/next-css" is already in "dependencies". Please remove existing entry first before adding it to "devDependencies".
success Saved 1 new dependency.
info Direct dependencies
└─ @zeit/next-css@1.0.1
info All dependencies
└─ @zeit/next-css@1.0.1
✨  Done in 1.85s.
const withCSS = require("@zeit/next-css");
if (typeof require !== "undefined") {
  require.extensions[".less"] = () => {};
  require.extensions[".css"] = (file) => {};
}

const config = {
  target: process.env.NODE_ENV !== 'production' ? 'server' : 'serverless',
  dontAutoRegisterSw: false,
  generateSw: false,
  devSwSrc: 'src/public/sw.js',
  workboxOpts: {
    swSrc: 'src/public/sw.js',
    swDest: 'src/public/service-worker.js'
  }
}

module.exports = withCSS(config)

としました。

$ yarn run dev                                      
yarn run v1.22.0
$ next
[ wait ]  starting the development server ...
[ info ]  waiting on http://localhost:3000 ...
[ info ]  bundled successfully, waiting for typecheck results...
[ wait ]  compiling ...
[ info ]  bundled successfully, waiting for typecheck results...
[ ready ] compiled successfully - ready on http://localhost:3000
[ event ] build page: /next/dist/pages/_error
[ wait ]  compiling ...
[ info ]  bundled successfully, waiting for typecheck results...
[ ready ] compiled successfully - ready on http://localhost:3000
[ event ] build page: /
[ wait ]  compiling ...
[ info ]  bundled successfully, waiting for typecheck results...
[ ready ] compiled successfully - ready on http://localhost:3000
[ info ]  bundled successfully, waiting for typecheck results...
[ ready ] compiled successfully - ready on http://localhost:3000

エラーがなくなりました。

http://localhost:3000 へアクセスしましょう。

f:id:poad1010:20200222205857p:plain

きたー!

f:id:poad1010:20200222205857p:plain

ユーザー登録(Create account をクリック)時に入力したメールアドレスに以下のようなワンタイムトークンが送られてくるため、 f:id:poad1010:20200222211622p:plain 遷移した画面で入力します。

すると。。。

f:id:poad1010:20200222205857p:plain

に戻ります。

ここで、登録したユーザー名とパスワードを入力して SIGN IN をクリックするとログイン出来ます。

4. サインアウト機能

さて、ここまでで Amplify と Cognitoを使用して、ローカルでサインイン出来るようになりました。

でも、サインアウト出来るようにしておかないとダメですよね?(滅多に使わないから期間過ぎたら自動的にサインアウトするように Cognito を設定しておけばよくね?というツッコミは無しで)

テキストリンクでも良いのですが、ボタンを配置してみましょう。

Material UI コンポーネントを追加します。

yarn add @material-ui/core @material-ui/icons
$ yarn add @material-ui/core @material-ui/icons   
yarn add v1.22.0
[1/4] 🔍  Resolving packages...
warning @material-ui/core > popper.js@1.16.1: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning "@zeit/next-css > css-loader@1.0.0" has unmet peer dependency "webpack@^4.0.0".
warning "@zeit/next-css > mini-css-extract-plugin@0.4.3" has unmet peer dependency "webpack@^4.4.0".
warning "aws-amplify > @aws-amplify/api@2.1.5" has incorrect peer dependency "@aws-amplify/pubsub@^1.2.4".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/analytics@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/api@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/auth@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/core@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/interactions@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/storage@^2.0.0".
warning " > aws-amplify-react@3.1.6" has unmet peer dependency "@aws-amplify/ui@^2.0.0".
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 21 new dependencies.
info Direct dependencies
├─ @material-ui/core@4.9.3
└─ @material-ui/icons@4.9.1
info All dependencies
├─ @emotion/hash@0.7.4
├─ @material-ui/core@4.9.3
├─ @material-ui/icons@4.9.1
├─ @material-ui/styles@4.9.0
├─ @material-ui/system@4.9.3
├─ @types/react-transition-group@4.2.3
├─ convert-css-length@2.0.1
├─ css-vendor@2.0.7
├─ dom-helpers@5.1.3
├─ hoist-non-react-statics@3.3.2
├─ hyphenate-style-name@1.0.3
├─ is-in-browser@1.1.3
├─ jss-plugin-camel-case@10.0.4
├─ jss-plugin-default-unit@10.0.4
├─ jss-plugin-global@10.0.4
├─ jss-plugin-nested@10.0.4
├─ jss-plugin-props-sort@10.0.4
├─ jss-plugin-rule-value-function@10.0.4
├─ jss-plugin-vendor-prefixer@10.0.4
├─ popper.js@1.16.1
└─ react-transition-group@4.3.0
✨  Done in 5.88s.

index.tsx には以下のように追加します。

Authentication

に記載されている通り、 Auth モジュールの signOut() を呼び出せば良いだけですね。

import React from 'react'
import Amplify, { Auth } from 'aws-amplify';
import config from '../aws-exports';
import Button from '@material-ui/core/Button';
import ExitToAppIcon from '@material-ui/icons/ExitToApp';

Amplify.configure(config);

class Home extends React.Component {

  logout() {
    Auth.signOut()
    .then(data => console.log(data))
    .catch(err => console.log(err));
  }

  render() {
    return (
      <div>
        <Button
          onClick={this.logout}
          variant="contained"
          color="primary"
          startIcon={<ExitToAppIcon />}
        >
          SIGN OUT
        </Button>

// (以下略)

f:id:poad1010:20200222213531p:plain

はい、出来ました。

5. amplify アプリの publish までの道のり

実は未だ、amplify アプリの publish は出来ません。

$ amplify publish

Please add hosting to your project before publishing your project
Command: amplify hosting add

5.1. amplify hosting add

amplify hosting add を行うと、以下のように聞かれます。

$ amplify hosting add
? Select the environment setup: (Use arrow keys)
❯ DEV (S3 only with HTTP) 
  PROD (S3 with CloudFront using HTTPS) 

とりえず、DEV を選択してCloudFront経由にしないようにして進めます。

$ amplify publish
✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name              | Operation | Provider plugin   |
| -------- | -------------------------- | --------- | ----------------- |
| Hosting  | S3AndCloudFront            | Create    | awscloudformation |
| Auth     | nextamplifyexample0dde88af | No Change | awscloudformation |
? Are you sure you want to continue? (Y/n) 

何か、CloudFront の文字が見えるような… まぁ、無料枠ならお金かからないから良いや。

$ amplify publish
✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name              | Operation | Provider plugin   |
| -------- | -------------------------- | --------- | ----------------- |
| Hosting  | S3AndCloudFront            | Create    | awscloudformation |
| Auth     | nextamplifyexample0dde88af | No Change | awscloudformation |
? Are you sure you want to continue? Yes
⠹ Updating resources in the cloud. This may take a few minutes...

UPDATE_COMPLETE    authnextamplifyexample0dde88af          AWS::CloudFormation::Stack Sat Feb 22 2020 21:46:27 GMT+0900 (日本標準時)                            
UPDATE_IN_PROGRESS authnextamplifyexample0dde88af          AWS::CloudFormation::Stack Sat Feb 22 2020 21:46:26 GMT+0900 (日本標準時)                            
CREATE_IN_PROGRESS hostingS3AndCloudFront                  AWS::CloudFormation::Stack Sat Feb 22 2020 21:46:26 GMT+0900 (日本標準時) Resource creation Initiated
CREATE_IN_PROGRESS hostingS3AndCloudFront                  AWS::CloudFormation::Stack Sat Feb 22 2020 21:46:25 GMT+0900 (日本標準時)                            
UPDATE_IN_PROGRESS amplify-next-amplify-example-dev-192739 AWS::CloudFormation::Stack Sat Feb 22 2020 21:46:22 GMT+0900 (日本標準時) User Initiated             
⠇ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS amplify-next-amplify-example-dev-192739-hostingS3AndCloudFront-153OK6UCXJPBA AWS::CloudFormation::Stack Sat Feb 22 2020 21:46:26 GMT+0900 (日本標準時) User Initiated
⠋ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS S3Bucket AWS::S3::Bucket Sat Feb 22 2020 21:46:29 GMT+0900 (日本標準時) 
⠋ Updating resources in the cloud. This may take a few minutes...

CREATE_IN_PROGRESS S3Bucket AWS::S3::Bucket Sat Feb 22 2020 21:46:30 GMT+0900 (日本標準時) Resource creation Initiated
⠙ Updating resources in the cloud. This may take a few minutes...

CREATE_COMPLETE amplify-next-amplify-example-dev-192739-hostingS3AndCloudFront-153OK6UCXJPBA AWS::CloudFormation::Stack Sat Feb 22 2020 21:46:53 GMT+0900 (日本標準時) 
CREATE_COMPLETE S3Bucket                                                                     AWS::S3::Bucket            Sat Feb 22 2020 21:46:52 GMT+0900 (日本標準時) 
⠹ Updating resources in the cloud. This may take a few minutes...

UPDATE_COMPLETE_CLEANUP_IN_PROGRESS amplify-next-amplify-example-dev-192739 AWS::CloudFormation::Stack Sat Feb 22 2020 21:47:02 GMT+0900 (日本標準時) 
CREATE_COMPLETE                     hostingS3AndCloudFront                  AWS::CloudFormation::Stack Sat Feb 22 2020 21:47:00 GMT+0900 (日本標準時) 
⠋ Updating resources in the cloud. This may take a few minutes...

UPDATE_COMPLETE amplify-next-amplify-example-dev-192739 AWS::CloudFormation::Stack Sat Feb 22 2020 21:47:03 GMT+0900 (日本標準時) 
UPDATE_COMPLETE authnextamplifyexample0dde88af          AWS::CloudFormation::Stack Sat Feb 22 2020 21:47:03 GMT+0900 (日本標準時) 
✔ All resources are updated in the cloud

Hosting endpoint: http://next-amplify-example-20200222214113-hostingbucket-dev.s3-website-us-west-2.amazonaws.com

yarn run v1.22.0
$ next build
Creating an optimized production build  

Compiled with warnings.

./node_modules/next/dist/next-server/server/load-components.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/load-components.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/load-components.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/load-components.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/require.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/require.js
Critical dependency: the request of a dependency is an expression

Automatically optimizing pages  

Page                                                           Size     First Load
┌ ○ /                                                          256 kB       608 kB
└   /_app                                                      46.4 kB      315 kB
+ shared by all                                                315 kB
  ├ static/_buildManifest.js                                   189 B
  ├ static/pages/_app.js                                       46.4 kB
  ├ chunks/242e76a06b3fa367d89c78b62af50ae3b459294c.a43009.js  11.7 kB
  ├ chunks/29107295.d41bbf.js                                  24.5 kB
  ├ chunks/c277677ab6f8ae068064b60157bc6d3cd2fd2c95.ea5b2a.js  173 kB
  ├ chunks/commons.af9b44.js                                   13 kB
  ├ chunks/framework.74d547.js                                 40.8 kB
  ├ chunks/styles.15b445.js                                    87 B
  ├ runtime/main.e744f1.js                                     4.74 kB
  └ runtime/webpack.4b444d.js                                  746 B

λ  (Lambda)  server-side renders at runtime (uses getInitialProps or getServerProps)
○  (Static)  automatically rendered as static HTML (uses no initial props)
●  (SSG)     automatically generated as static HTML + JSON (uses getStaticProps)

✨  Done in 15.16s.
frontend build command exited with code 0

Cannot find the distribution folder.
Distribution folder is currently set as:
  /Users/ken-yo/next-amplify-example/build

Cannot find the distribution folder.
Error: Cannot find the distribution folder.
    at Object.scan (/usr/local/lib/node_modules/@aws-amplify/cli/node_modules/amplify-category-hosting/lib/S3AndCloudFront/helpers/file-scanner.js:38:11)
    at Object.run (/usr/local/lib/node_modules/@aws-amplify/cli/node_modules/amplify-category-hosting/lib/S3AndCloudFront/helpers/file-uploader.js:16:32)
    at Object.publish (/usr/local/lib/node_modules/@aws-amplify/cli/node_modules/amplify-category-hosting/lib/S3AndCloudFront/index.js:116:6)
    at Object.runServiceAction (/usr/local/lib/node_modules/@aws-amplify/cli/node_modules/amplify-category-hosting/lib/category-manager.js:63:31)
    at Object.publish (/usr/local/lib/node_modules/@aws-amplify/cli/node_modules/amplify-category-hosting/index.js:69:30)
    at publishToHostingBucket (/usr/local/lib/node_modules/@aws-amplify/cli/node_modules/amplify-frontend-javascript/lib/publisher.js:22:30)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

コケて上手く行きません…

build ディレクトリーが無いと言われてますね。

以下のように修正しました。

{
  "name": "next-amplify-example",
  "version": "1.0.0",
  "private": true,
  "license": "MIT",
  "scripts": {
    "test": "jest --passWithNoTests",
    "dev": "next",
    "build": "next build && next export && rm -rf build && mv out build",
    "start": "next start",
    "lint": "eslint --ext .ts,.js,.tsx,.jsx src"
  },
  "lint-staged": {
    "src/*.{ts,js,tsx,jsx}": "eslint"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "dependencies": {
    "@material-ui/core": "^4.9.3",
    "@material-ui/icons": "^4.9.1",
    "@types/node": "^13.7.4",
    "@types/react": "^16.9.22",
    "@zeit/next-css": "^1.0.1",
    "aws-amplify": "^2.2.5",
    "aws-amplify-react": "^3.1.6",
    "next": "^9.2.2",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  },
  "devDependencies": {
    "typescript": "^3.8.2"
  }
}

"build": "next build && next export && rm -rf build && mv out build" がポイントですね。

$ amplify publish
✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name              | Operation | Provider plugin   |
| -------- | -------------------------- | --------- | ----------------- |
| Auth     | nextamplifyexample0dde88af | No Change | awscloudformation |
| Hosting  | S3AndCloudFront            | No Change | awscloudformation |

No changes detected
yarn run v1.22.0
$ next build && next export && mv out build
Creating an optimized production build  

Compiled with warnings.

./node_modules/next/dist/next-server/server/load-components.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/load-components.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/load-components.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/load-components.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/require.js
Critical dependency: the request of a dependency is an expression

./node_modules/next/dist/next-server/server/require.js
Critical dependency: the request of a dependency is an expression

Automatically optimizing pages  

Page                                                           Size     First Load
┌ ○ /                                                          256 kB       608 kB
└   /_app                                                      46.4 kB      315 kB
+ shared by all                                                315 kB
  ├ static/_buildManifest.js                                   189 B
  ├ static/pages/_app.js                                       46.4 kB
  ├ chunks/29107295.6cce3a.js                                  24.5 kB
  ├ chunks/5d3dd53bfefaaba7d132131a0ba097ceb6bf38d5.961381.js  11.7 kB
  ├ chunks/79d124448b8210c46ac0124c1c9c03bef3930399.ea5b2a.js  173 kB
  ├ chunks/commons.af9b44.js                                   13 kB
  ├ chunks/framework.74d547.js                                 40.8 kB
  ├ chunks/styles.15b445.js                                    87 B
  ├ runtime/main.d64045.js                                     4.74 kB
  └ runtime/webpack.4b444d.js                                  746 B

λ  (Lambda)  server-side renders at runtime (uses getInitialProps or getServerProps)
○  (Static)  automatically rendered as static HTML (uses no initial props)
●  (SSG)     automatically generated as static HTML + JSON (uses getStaticProps)

> using build directory: /Users/ken-yo/next-amplify-example/.next
  copying "static build" directory
> No "exportPathMap" found in "next.config.js". Generating map from "./pages"
  launching 15 workers
Exporting (5/5)

Export successful
✨  Done in 11.93s.
frontend build command exited with code 0
✔ Uploaded files successfully.
Your app is published successfully.
http://next-amplify-example-20200222214113-hostingbucket-dev.s3-website-us-west-2.amazonaws.com

f:id:poad1010:20200222225034p:plain

出来ました!

ただ、 GitHub - poad/next-ts-template のブランチを元に作成した場合、next.config.js を次のように修正しないと、 aws-amplify-react の Global Stylesheet 関連の処理やら styled-jsx やらとの兼ね合いでコケました。(styled-jsx は結果的に使っていないので yarn remove しました)

因みに、 yarn run dev ではコケずに yarn build でだけコケるとかあったのでご注意を!

const withCSS = require('@zeit/next-css')
if (typeof require !== "undefined") {
  require.extensions[".less"] = () => { };
  require.extensions[".css"] = (file) => { };
}

const resolve = require('resolve')
global.navigator = () => null
const config = {
  webpack(config, options) {
    const { dir, isServer } = options
    config.externals = []
    if (isServer) {
      config.externals.push((context, request, callback) => {
        resolve(request, { basedir: dir, preserveSymlinks: true }, (err, res) => {
          if (err) {
            return callback()
          }
          if (
            res.match(/node_modules[/\\].*\.css/)
            && !res.match(/node_modules[/\\]webpack/)
            && !res.match(/node_modules[/\\]@aws-amplify/)
          ) {
            return callback(null, `css ${request}`)
          }

          callback()
        })
      })
    }

    return config
  }
}

module.exports = withCSS(config)

と、なかなかハマりどころ多いですが、Nuxt.js(Vue.js) + TypeScript + amplify よりは簡単そうです。

next.config.js の修正が不味いケース

その1

$ yarn build
yarn run v1.22.0
$ next build
Creating an optimized production build  

Compiled successfully.

Automatically optimizing pages ..Service Worker Loaded...
> Build error occurred
ReferenceError: self is not defined
    at Object.C+qz (/Users/ken-yo/git/es-examples/next-examples/next-amplify-example/.next/server/static/23FhuuakIG-ehoL-uwoCM/pages/sw.js:108:1)
    at __webpack_require__ (/Users/ken-yo/git/es-examples/next-examples/next-amplify-example/.next/server/static/23FhuuakIG-ehoL-uwoCM/pages/sw.js:23:31)
    at Object.3 (/Users/ken-yo/git/es-examples/next-examples/next-amplify-example/.next/server/static/23FhuuakIG-ehoL-uwoCM/pages/sw.js:99:18)
    at __webpack_require__ (/Users/ken-yo/git/es-examples/next-examples/next-amplify-example/.next/server/static/23FhuuakIG-ehoL-uwoCM/pages/sw.js:23:31)
    at /Users/ken-yo/git/es-examples/next-examples/next-amplify-example/.next/server/static/23FhuuakIG-ehoL-uwoCM/pages/sw.js:91:18
    at Object.<anonymous> (/Users/ken-yo/git/es-examples/next-examples/next-amplify-example/.next/server/static/23FhuuakIG-ehoL-uwoCM/pages/sw.js:94:10)
    at Module._compile (internal/modules/cjs/loader.js:1151:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1171:10)
    at Module.load (internal/modules/cjs/loader.js:1000:32)
    at Function.Module._load (internal/modules/cjs/loader.js:899:14) {
  type: 'ReferenceError'
}
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

とりあえず、next.config.js の中の記述で service worker周りの設定を削って、上述の一番最後に書いた next.config.js へ置き換えてみてください。

その2

$ yarn run dev
yarn run v1.22.0
$ next
[ wait ]  starting the development server ...
[ info ]  waiting on http://localhost:3000 ...
> Using external babel configuration
> Location: "/Users/ken-yo/git/es-examples/next-examples/next-amplify-example/.babelrc"
[ error ] ./node_modules/@aws-amplify/ui/dist/style.css
Global CSS cannot be imported from within node_modules.
Read more: https://err.sh/next.js/css-npm
Location: node_modules/aws-amplify-react/lib-esm/Amplify-UI/Amplify-UI-Components-React.js

Next.js 公式では非推奨の @zeit/next-css を追加して…という上述の記事に記載している next.config.js を設定してください。

今回作成したサンプルコード

github.com

テンプレートリポジトリーをベースにしたもの

github.com

Scala で AWS CDK Javaを使ってみる

はじめに

Scala Advent Calendar 2019 - Qiita の 16日目への寄稿です。

AWS CDK が、 2019年7月11日 に Generally Available となりました。
そして、2019年11月26日に AWS CDK for Java も Generally Available となりました。
Scalaから使ったら更に読み書きし易くなったりしないものだろうか?と思い立ったので試してみます。

環境構築

AWS CDK の Getting Started Prerequisites を見ると、 Node.js が必要とのこと。
マシンのOSに依存させるのは嫌なので、ここではDockerイメージ内で作業することとします。

実行方法は、GitHubリポジトリーのREADMEをご覧ください。

いざ!CDK

実行したコンテナー内で cdk init --language java を実行します。

[zsh@d535e914ac9e cdk]$ dk init --language java
Applying project template app for java
Initializing a new git repository...

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: unable to auto-detect email address (got 'zsh@be05ff957502.(none)')
Unable to initialize git repository for your project.
Executing mvn package...
# Welcome to your CDK Java project!

This is a blank project for Java development with CDK.

The `cdk.json` file tells the CDK Toolkit how to execute your app.

It is a [Maven](https://maven.apache.org/) based project, so you can open this project with any Maven compatible Java IDE to build and run tests.

## Useful commands

 * `mvn package`     compile and run tests
 * `cdk ls`          list all stacks in the app
 * `cdk synth`       emits the synthesized CloudFormation template
 * `cdk deploy`      deploy this stack to your default AWS account/region
 * `cdk diff`        compare deployed stack with current state
 * `cdk docs`        open CDK documentation

Enjoy!

[zsh@d535e914ac9e cdk (master +)]$ 

これで、 pom.xml が出来ました。

[zsh@d535e914ac9e cdk (master +)]$ ls
src  target  README.md  cdk.json  pom.xml
[zsh@d535e914ac9e cdk (master +)]$ 

pomの中身とCDKのバージョン

作られたpom.xmlを見てみると、グループIDは software.amazon.awscdk、バージョンはCDK本体のバージョンの後ろに .DEVPREVIEW が付与された形。
おそらくGAになったら外れるでしょう。

[zsh@d535e914ac9e cdk (master +)]$ cat pom.xml 
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
         xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.myorg</groupId>
    <artifactId>cdk</artifactId>
    <version>0.1</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <configuration>
                    <mainClass>com.myorg.CdkApp</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- AWS Cloud Development Kit -->
        <dependency>
            <groupId>software.amazon.awscdk</groupId>
            <artifactId>core</artifactId>
            <version>1.18.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
[zsh@d535e914ac9e cdk (master +)]$ cdk --version
1.18.0 (build bc924bc)
[zsh@d535e914ac9e cdk (master +)]$ 

sbtへ移植

new project

[zsh@73d67aca507d ~]$ sbt new scala/hello-world.g8
[info] Set current project to zsh (in build file:/home/zsh/)
[info] Set current project to zsh (in build file:/home/zsh/)

A template to demonstrate a minimal Scala application 

name [Hello World template]: 
Template applied in /home/zsh/./cdk

[zsh@73d67aca507d ~]$ ls
cdk  target
[zsh@73d67aca507d ~]$ cd cdk 
[zsh@73d67aca507d cdk (master +%)]$ ls
project  src  target  README.md  build.sbt  cdk.json  pom.xml
[zsh@73d67aca507d cdk (master +%)]$ 

これで、CDKを使うアプリの本体のベースが出来ました。
build.sbt へ pom.xml の内容を記述して、Scalaのコードを記述していきます。
build.sbt はこんな感じにしました。

import sbt.Keys.{libraryDependencies, scalaVersion}

scalaVersion := "2.13.1"

name := "hello-world"
organization := "com.github.poad"
version := "1.0"

val cdkVersion = "1.18.0"
libraryDependencies ++= Seq(
    "software.amazon.awscdk" % "core" % cdkVersion
)

流石に Java のコードを Scala へ移植する部分や、Javdocを調べてScalaのコードを記述する部分の説明については割愛します。

cdk.json

Getting Started With the AWS CDK - AWS Cloud Development Kit (AWS CDK) の 「cdk ls」を実行すると以下のようなエラーが出力されます。 (Javaのクラスを残したままだと発生しないかと思います。。。)

[zsh@bd8ee7c9cbb6 cdk (master *+)]$ cdk ls
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.6.0:java (default-cli) on project cdk: An exception occured while executing the Java class. com.myorg.HelloApp -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
Subprocess exited with error 1

あるいは、pom.xml を削除していると、以下のようなエラーが出力されます。

[zsh@bd8ee7c9cbb6 cdk (master *+%)]$ cdk ls
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.6.0:java (default-cli): Goal requires a project to execute but there is no POM in this directory (/home/zsh/cdk). Please verify you invoked Maven from the correct directory. -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MissingProjectException
Subprocess exited with error 1

これらの原因は、cdk init で生成される cdk.json というファイルです。

{
  "app": "mvn -e -q exec:java",
  "context": {
    "@aws-cdk/core:enableStackNameDuplicates": "true"
  }
}

以下のように書き換えます。

{
  "app": "sbt run",
  "context": {
    "@aws-cdk/core:enableStackNameDuplicates": "true"
  }
}

これで正しく動くようになります。

[zsh@bd8ee7c9cbb6 cdk (master *+%)]$ cdk ls
[info] [launcher] getting org.scala-sbt sbt 1.3.4  (this may take some time)...
downloading https://repo1.maven.org/maven2/org/scala-sbt/sbt/1.3.4/sbt-1.3.4.jar ...
:: loading settings :: url = jar:file:/usr/local/sbt/bin/sbt-launch.jar!/org/apache/ivy/core/settings/ivysettings.xml
downloading https://repo1.maven.org/maven2/org/scala-sbt/main_2.12/1.3.4/main_2.12-1.3.4.jar ...
:: loading settings :: url = jar:file:/usr/local/sbt/bin/sbt-launch.jar!/org/apache/ivy/core/settings/ivysettings.xml
downloading https://repo1.maven.org/maven2/org/scala-sbt/io_2.12/1.3.1/io_2.12-1.3.1.jar ...
downloading https://repo1.maven.org/maven2/org/scala-lang/scala-library/2.12.10/scala-library-2.12.10.jar ...
:: loading settings :: url = jar:file:/usr/local/sbt/bin/sbt-launch.jar!/org/apache/ivy/core/settings/ivysettings.xml
:: loading settings :: url = jar:file:/usr/local/sbt/bin/sbt-launch.jar!/org/apache/ivy/core/settings/ivysettings.xml
    [SUCCESSFUL ] org.scala-sbt#sbt;1.3.4!sbt.jar (1087ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/logic_2.12/1.3.4/logic_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#logic_2.12;1.3.4!logic_2.12.jar (460ms)
    [SUCCESSFUL ] org.scala-sbt#io_2.12;1.3.1!io_2.12.jar (1526ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/main-settings_2.12/1.3.4/main-settings_2.12-1.3.4.jar ...
downloading https://repo1.maven.org/maven2/org/scala-sbt/actions_2.12/1.3.4/actions_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#actions_2.12;1.3.4!actions_2.12.jar (595ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/run_2.12/1.3.4/run_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#main-settings_2.12;1.3.4!main-settings_2.12.jar (612ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/command_2.12/1.3.4/command_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#run_2.12;1.3.4!run_2.12.jar (452ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/collections_2.12/1.3.4/collections_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#command_2.12;1.3.4!command_2.12.jar (466ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/scripted-sbt-redux_2.12/1.3.4/scripted-sbt-redux_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-lang#scala-library;2.12.10!scala-library.jar (2808ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/scripted-plugin_2.12/1.3.4/scripted-plugin_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#scripted-sbt-redux_2.12;1.3.4!scripted-sbt-redux_2.12.jar (450ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc-lm-integration_2.12/1.3.4/zinc-lm-integration_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#collections_2.12;1.3.4!collections_2.12.jar (569ms)
downloading https://repo1.maven.org/maven2/org/scala-lang/modules/scala-xml_2.12/1.2.0/scala-xml_2.12-1.2.0.jar ...
    [SUCCESSFUL ] org.scala-sbt#scripted-plugin_2.12;1.3.4!scripted-plugin_2.12.jar (441ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/launcher-interface/1.1.3/launcher-interface-1.1.3.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc-lm-integration_2.12;1.3.4!zinc-lm-integration_2.12.jar (450ms)
downloading https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.11.2/log4j-api-2.11.2.jar ...
    [SUCCESSFUL ] org.scala-sbt#launcher-interface;1.1.3!launcher-interface.jar (454ms)
downloading https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.11.2/log4j-core-2.11.2.jar ...
    [SUCCESSFUL ] org.scala-lang.modules#scala-xml_2.12;1.2.0!scala-xml_2.12.jar(bundle) (685ms)
downloading https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.11.2/log4j-slf4j-impl-2.11.2.jar ...
    [SUCCESSFUL ] org.apache.logging.log4j#log4j-api;2.11.2!log4j-api.jar (465ms)
downloading https://repo1.maven.org/maven2/com/github/cb372/scalacache-caffeine_2.12/0.20.0/scalacache-caffeine_2.12-0.20.0.jar ...
    [SUCCESSFUL ] org.apache.logging.log4j#log4j-slf4j-impl;2.11.2!log4j-slf4j-impl.jar (469ms)
downloading https://repo1.maven.org/maven2/io/get-coursier/lm-coursier-shaded_2.12/2.0.0-RC5-2/lm-coursier-shaded_2.12-2.0.0-RC5-2.jar ...
    [SUCCESSFUL ] com.github.cb372#scalacache-caffeine_2.12;0.20.0!scalacache-caffeine_2.12.jar (449ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/util-logging_2.12/1.3.2/util-logging_2.12-1.3.2.jar ...
    [SUCCESSFUL ] org.apache.logging.log4j#log4j-core;2.11.2!log4j-core.jar (811ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/librarymanagement-core_2.12/1.3.0/librarymanagement-core_2.12-1.3.0.jar ...
    [SUCCESSFUL ] org.scala-sbt#util-logging_2.12;1.3.2!util-logging_2.12.jar (476ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/librarymanagement-ivy_2.12/1.3.0/librarymanagement-ivy_2.12-1.3.0.jar ...
    [SUCCESSFUL ] org.scala-sbt#librarymanagement-core_2.12;1.3.0!librarymanagement-core_2.12.jar (601ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/compiler-interface/1.3.1/compiler-interface-1.3.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#compiler-interface;1.3.1!compiler-interface.jar (456ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc-compile_2.12/1.3.1/zinc-compile_2.12-1.3.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#librarymanagement-ivy_2.12;1.3.0!librarymanagement-ivy_2.12.jar (687ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/util-relation_2.12/1.3.2/util-relation_2.12-1.3.2.jar ...
    [SUCCESSFUL ] org.scala-sbt#util-relation_2.12;1.3.2!util-relation_2.12.jar (437ms)
downloading https://repo1.maven.org/maven2/com/eed3si9n/sjson-new-scalajson_2.12/0.8.2/sjson-new-scalajson_2.12-0.8.2.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc-compile_2.12;1.3.1!zinc-compile_2.12.jar (445ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/util-position_2.12/1.3.2/util-position_2.12-1.3.2.jar ...
    [SUCCESSFUL ] com.eed3si9n#sjson-new-scalajson_2.12;0.8.2!sjson-new-scalajson_2.12.jar (448ms)
downloading https://repo1.maven.org/maven2/com/eed3si9n/shaded-scalajson_2.12/1.0.0-M4/shaded-scalajson_2.12-1.0.0-M4.jar ...
    [SUCCESSFUL ] org.scala-sbt#util-position_2.12;1.3.2!util-position_2.12.jar (456ms)
downloading https://repo1.maven.org/maven2/org/spire-math/jawn-parser_2.12/0.10.4/jawn-parser_2.12-0.10.4.jar ...
    [SUCCESSFUL ] com.eed3si9n#shaded-scalajson_2.12;1.0.0-M4!shaded-scalajson_2.12.jar (463ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/completion_2.12/1.3.4/completion_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.spire-math#jawn-parser_2.12;0.10.4!jawn-parser_2.12.jar (468ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/task-system_2.12/1.3.4/task-system_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#completion_2.12;1.3.4!completion_2.12.jar (448ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/tasks_2.12/1.3.4/tasks_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#task-system_2.12;1.3.4!task-system_2.12.jar (524ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/testing_2.12/1.3.4/testing_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#tasks_2.12;1.3.4!tasks_2.12.jar (455ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/util-tracking_2.12/1.3.2/util-tracking_2.12-1.3.2.jar ...
    [SUCCESSFUL ] org.scala-sbt#testing_2.12;1.3.4!testing_2.12.jar (465ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc-classpath_2.12/1.3.1/zinc-classpath_2.12-1.3.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#util-tracking_2.12;1.3.2!util-tracking_2.12.jar (443ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc-apiinfo_2.12/1.3.1/zinc-apiinfo_2.12-1.3.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc-classpath_2.12;1.3.1!zinc-classpath_2.12.jar (459ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc_2.12/1.3.1/zinc_2.12-1.3.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc-apiinfo_2.12;1.3.1!zinc-apiinfo_2.12.jar (453ms)
downloading https://repo1.maven.org/maven2/jline/jline/2.14.6/jline-2.14.6.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc_2.12;1.3.1!zinc_2.12.jar (448ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/util-control_2.12/1.3.2/util-control_2.12-1.3.2.jar ...
    [SUCCESSFUL ] org.scala-sbt#main_2.12;1.3.4!main_2.12.jar (9125ms)
downloading https://repo1.maven.org/maven2/com/swoval/file-tree-views/2.1.3/file-tree-views-2.1.3.jar ...
    [SUCCESSFUL ] jline#jline;2.14.6!jline.jar (447ms)
downloading https://repo1.maven.org/maven2/net/java/dev/jna/jna/4.5.0/jna-4.5.0.jar ...
    [SUCCESSFUL ] org.scala-sbt#util-control_2.12;1.3.2!util-control_2.12.jar (451ms)
downloading https://repo1.maven.org/maven2/net/java/dev/jna/jna-platform/4.5.0/jna-platform-4.5.0.jar ...
    [SUCCESSFUL ] net.java.dev.jna#jna;4.5.0!jna.jar (904ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/util-interface/1.3.2/util-interface-1.3.2.jar ...
    [SUCCESSFUL ] com.swoval#file-tree-views;2.1.3!file-tree-views.jar (1016ms)
downloading https://repo1.maven.org/maven2/com/eed3si9n/sjson-new-core_2.12/0.8.3/sjson-new-core_2.12-0.8.3.jar ...
    [SUCCESSFUL ] net.java.dev.jna#jna-platform;4.5.0!jna-platform.jar (839ms)
downloading https://repo1.maven.org/maven2/com/lmax/disruptor/3.4.2/disruptor-3.4.2.jar ...
    [SUCCESSFUL ] org.scala-sbt#util-interface;1.3.2!util-interface.jar (434ms)
downloading https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar ...
    [SUCCESSFUL ] com.eed3si9n#sjson-new-core_2.12;0.8.3!sjson-new-core_2.12.jar (577ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/util-cache_2.12/1.3.2/util-cache_2.12-1.3.2.jar ...
    [SUCCESSFUL ] com.lmax#disruptor;3.4.2!disruptor.jar (676ms)
downloading https://repo1.maven.org/maven2/com/eed3si9n/sjson-new-murmurhash_2.12/0.8.3/sjson-new-murmurhash_2.12-0.8.3.jar ...
    [SUCCESSFUL ] com.eed3si9n#sjson-new-murmurhash_2.12;0.8.3!sjson-new-murmurhash_2.12.jar (444ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/test-agent/1.3.4/test-agent-1.3.4.jar ...
    [SUCCESSFUL ] io.get-coursier#lm-coursier-shaded_2.12;2.0.0-RC5-2!lm-coursier-shaded_2.12.jar (7176ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar ...
    [SUCCESSFUL ] com.google.protobuf#protobuf-java;3.7.0!protobuf-java.jar(bundle) (1011ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/compiler-bridge_2.12/1.3.1/compiler-bridge_2.12-1.3.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#util-cache_2.12;1.3.2!util-cache_2.12.jar (987ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc-classfile_2.12/1.3.1/zinc-classfile_2.12-1.3.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#test-agent;1.3.4!test-agent.jar (449ms)
downloading https://repo1.maven.org/maven2/com/jcraft/jsch/0.1.54/jsch-0.1.54.jar ...
    [SUCCESSFUL ] org.scala-sbt#test-interface;1.0!test-interface.jar (443ms)
downloading https://repo1.maven.org/maven2/com/eed3si9n/gigahorse-okhttp_2.12/0.5.0/gigahorse-okhttp_2.12-0.5.0.jar ...
    [SUCCESSFUL ] org.scala-sbt#compiler-bridge_2.12;1.3.1!compiler-bridge_2.12.jar (446ms)
downloading https://repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-urlconnection/3.7.0/okhttp-urlconnection-3.7.0.jar ...
    [SUCCESSFUL ] com.jcraft#jsch;0.1.54!jsch.jar (459ms)
downloading https://repo1.maven.org/maven2/com/eed3si9n/gigahorse-core_2.12/0.5.0/gigahorse-core_2.12-0.5.0.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc-classfile_2.12;1.3.1!zinc-classfile_2.12.jar (670ms)
downloading https://repo1.maven.org/maven2/com/squareup/okhttp3/okhttp/3.14.2/okhttp-3.14.2.jar ...
    [SUCCESSFUL ] com.eed3si9n#gigahorse-okhttp_2.12;0.5.0!gigahorse-okhttp_2.12.jar (452ms)
downloading https://repo1.maven.org/maven2/com/typesafe/ssl-config-core_2.12/0.4.0/ssl-config-core_2.12-0.4.0.jar ...
    [SUCCESSFUL ] com.squareup.okhttp3#okhttp-urlconnection;3.7.0!okhttp-urlconnection.jar (447ms)
downloading https://repo1.maven.org/maven2/org/reactivestreams/reactive-streams/1.0.2/reactive-streams-1.0.2.jar ...
    [SUCCESSFUL ] com.eed3si9n#gigahorse-core_2.12;0.5.0!gigahorse-core_2.12.jar (475ms)
downloading https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.26/slf4j-api-1.7.26.jar ...
    [SUCCESSFUL ] org.reactivestreams#reactive-streams;1.0.2!reactive-streams.jar (454ms)
downloading https://repo1.maven.org/maven2/org/scala-lang/modules/scala-parser-combinators_2.12/1.1.2/scala-parser-combinators_2.12-1.1.2.jar ...
    [SUCCESSFUL ] com.typesafe#ssl-config-core_2.12;0.4.0!ssl-config-core_2.12.jar(bundle) (1013ms)
downloading https://repo1.maven.org/maven2/com/typesafe/config/1.3.3/config-1.3.3.jar ...
    [SUCCESSFUL ] org.slf4j#slf4j-api;1.7.26!slf4j-api.jar (783ms)
downloading https://repo1.maven.org/maven2/com/squareup/okio/okio/1.17.2/okio-1.17.2.jar ...
    [SUCCESSFUL ] org.scala-lang.modules#scala-parser-combinators_2.12;1.1.2!scala-parser-combinators_2.12.jar(bundle) (690ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc-core_2.12/1.3.1/zinc-core_2.12-1.3.1.jar ...
    [SUCCESSFUL ] com.squareup.okhttp3#okhttp;3.14.2!okhttp.jar (1709ms)
    [SUCCESSFUL ] com.typesafe#config;1.3.3!config.jar(bundle) (693ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc-persist_2.12/1.3.1/zinc-persist_2.12-1.3.1.jar ...
downloading https://repo1.maven.org/maven2/org/scala-sbt/zinc-compile-core_2.12/1.3.1/zinc-compile-core_2.12-1.3.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc-core_2.12;1.3.1!zinc-core_2.12.jar (788ms)
downloading https://repo1.maven.org/maven2/com/trueaccord/scalapb/scalapb-runtime_2.12/0.6.0/scalapb-runtime_2.12-0.6.0.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc-compile-core_2.12;1.3.1!zinc-compile-core_2.12.jar (672ms)
downloading https://repo1.maven.org/maven2/com/trueaccord/lenses/lenses_2.12/0.4.12/lenses_2.12-0.4.12.jar ...
    [SUCCESSFUL ] com.squareup.okio#okio;1.17.2!okio.jar (1405ms)
downloading https://repo1.maven.org/maven2/com/lihaoyi/fastparse_2.12/0.4.2/fastparse_2.12-0.4.2.jar ...
    [SUCCESSFUL ] com.trueaccord.lenses#lenses_2.12;0.4.12!lenses_2.12.jar (481ms)
downloading https://repo1.maven.org/maven2/com/lihaoyi/fastparse-utils_2.12/0.4.2/fastparse-utils_2.12-0.4.2.jar ...
    [SUCCESSFUL ] com.lihaoyi#fastparse_2.12;0.4.2!fastparse_2.12.jar (635ms)
downloading https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.12/0.1.3/sourcecode_2.12-0.1.3.jar ...
    [SUCCESSFUL ] com.lihaoyi#fastparse-utils_2.12;0.4.2!fastparse-utils_2.12.jar (478ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/sbinary_2.12/0.5.0/sbinary_2.12-0.5.0.jar ...
    [SUCCESSFUL ] com.trueaccord.scalapb#scalapb-runtime_2.12;0.6.0!scalapb-runtime_2.12.jar (1505ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/core-macros_2.12/1.3.4/core-macros_2.12-1.3.4.jar ...
    [SUCCESSFUL ] com.lihaoyi#sourcecode_2.12;0.1.3!sourcecode_2.12.jar (482ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/protocol_2.12/1.3.4/protocol_2.12-1.3.4.jar ...
    [SUCCESSFUL ] org.scala-sbt#core-macros_2.12;1.3.4!core-macros_2.12.jar (518ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/template-resolver/0.1/template-resolver-0.1.jar ...
    [SUCCESSFUL ] org.scala-sbt#sbinary_2.12;0.5.0!sbinary_2.12.jar (743ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/ipcsocket/ipcsocket/1.0.0/ipcsocket-1.0.0.jar ...
    [SUCCESSFUL ] org.scala-sbt#template-resolver;0.1!template-resolver.jar (441ms)
downloading https://repo1.maven.org/maven2/org/scala-lang/scala-compiler/2.12.10/scala-compiler-2.12.10.jar ...
    [SUCCESSFUL ] org.scala-sbt.ipcsocket#ipcsocket;1.0.0!ipcsocket.jar (455ms)
downloading https://repo1.maven.org/maven2/org/scala-lang/scala-reflect/2.12.10/scala-reflect-2.12.10.jar ...
    [SUCCESSFUL ] org.scala-sbt#protocol_2.12;1.3.4!protocol_2.12.jar (1202ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/util-scripted_2.12/1.3.2/util-scripted_2.12-1.3.2.jar ...
    [SUCCESSFUL ] org.scala-sbt#util-scripted_2.12;1.3.2!util-scripted_2.12.jar (450ms)
downloading https://repo1.maven.org/maven2/com/github/cb372/scalacache-core_2.12/0.20.0/scalacache-core_2.12-0.20.0.jar ...
    [SUCCESSFUL ] com.github.cb372#scalacache-core_2.12;0.20.0!scalacache-core_2.12.jar (457ms)
downloading https://repo1.maven.org/maven2/com/github/ben-manes/caffeine/caffeine/2.5.6/caffeine-2.5.6.jar ...
    [SUCCESSFUL ] com.github.ben-manes.caffeine#caffeine;2.5.6!caffeine.jar (1017ms)
downloading https://repo1.maven.org/maven2/org/scala-sbt/ivy/ivy/2.3.0-sbt-cb9cc189e9f3af519f9f102e6c5d446488ff6832/ivy-2.3.0-sbt-cb9cc189e9f3af519f9f102e6c5d446488ff6832.jar ...
    [SUCCESSFUL ] org.scala-sbt#zinc-persist_2.12;1.3.1!zinc-persist_2.12.jar (5683ms)
    [SUCCESSFUL ] org.scala-sbt.ivy#ivy;2.3.0-sbt-cb9cc189e9f3af519f9f102e6c5d446488ff6832!ivy.jar (1037ms)
    [SUCCESSFUL ] org.scala-lang#scala-reflect;2.12.10!scala-reflect.jar (6224ms)
    [SUCCESSFUL ] org.scala-lang#scala-compiler;2.12.10!scala-compiler.jar (8766ms)
:: retrieving :: org.scala-sbt#boot-app
    confs: [default]
    81 artifacts copied, 0 already retrieved
[info] [launcher] getting Scala 2.12.10 (for sbt)...
downloading https://repo1.maven.org/maven2/org/scala-lang/modules/scala-xml_2.12/1.0.6/scala-xml_2.12-1.0.6.jar ...
downloading https://repo1.maven.org/maven2/org/fusesource/jansi/jansi/1.12/jansi-1.12.jar ...
    [SUCCESSFUL ] org.fusesource.jansi#jansi;1.12!jansi.jar (1318ms)
    [SUCCESSFUL ] org.scala-lang.modules#scala-xml_2.12;1.0.6!scala-xml_2.12.jar(bundle) (2602ms)
:: retrieving :: org.scala-sbt#boot-scala
    confs: [default]
    6 artifacts copied, 0 already retrieved
[info] Loading project definition from /home/zsh/cdk/project
[info] Loading settings for project cdk from build.sbt ...
[info] Set current project to hello-world (in build file:/home/zsh/cdk/)
[warn] There may be incompatibilities among your library dependencies; run 'evicted' to see detailed eviction warnings.
[info] Compiling 2 Scala sources to /home/zsh/cdk/target/scala-2.13/classes ...
[info] Non-compiled module 'compiler-bridge_2.13' for Scala 2.13.1. Compiling...
8 warnings found
[info]   Compilation completed in 7.349s.
[info] Done compiling.
[info] running com.myorg.CdkApp 
[success] Total time: 27 s, completed Dec 10, 2019 2:32:33 PM
CdkStack
[zsh@bd8ee7c9cbb6 cdk (master *+%)]$ 

cdk.json の記述を変えれば Gradle などでも動きそうですね。

cdk deploy

[zsh@bd8ee7c9cbb6 cdk (master *+%)]$cdk deploy
[info] Loading project definition from /home/zsh/cdk/project
[info] Loading settings for project cdk from build.sbt ...
[info] Set current project to hello-world (in build file:/home/zsh/cdk/)
[info] running com.myorg.CdkApp 
[success] Total time: 2 s, completed Dec 10, 2019 2:33:53 PM
CdkStack: deploying...
CdkStack: creating CloudFormation changeset...
 0/2 | 2:34:07 PM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata 
 0/2 | 2:34:09 PM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata Resource creation Initiated
 1/2 | 2:34:09 PM | CREATE_COMPLETE      | AWS::CDK::Metadata | CDKMetadata 
 2/2 | 2:34:11 PM | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CdkStack 

 ✅  CdkStack

Stack ARN:
arn:aws:cloudformation:us-west-2:XXXXXXXXXXXX:stack/CdkStack/xxxxxxxx-xxxx-xxxx-xxxx-067aa3cae1e0
[zsh@bd8ee7c9cbb6 cdk (master *+%)]$ 

これで CloudFormation Stack が出来ました。 f:id:poad1010:20191210233704p:plain

当然ながら、それで作られるリソースも居ます。 f:id:poad1010:20191210233810p:plain

cdk destroy

cdkで作ったStackやリソースを削除するには、 cdk destroy を使います。

[zsh@bd8ee7c9cbb6 cdk (master *+%)]$ cdk destroy
[info] Loading project definition from /home/zsh/cdk/project
[info] Loading settings for project cdk from build.sbt ...
[info] Set current project to hello-world (in build file:/home/zsh/cdk/)
[info] running com.myorg.CdkApp 
[success] Total time: 2 s, completed Dec 10, 2019 2:38:36 PM
Are you sure you want to delete: CdkStack (y/n)? y
CdkStack: destroying...

 ✅  CdkStack: destroyed
[zsh@bd8ee7c9cbb6 cdk (master *+%)]$ 

これで CloudFormation Stack が削除されました。 f:id:poad1010:20191210234013p:plain f:id:poad1010:20191210234024p:plain

流石に CloudFormation や CDK の API は調べていただくということで、説明はここまでとなります。

まとめ

以下の設定を行うだけで、とても簡単にAWS CDK を Scala で使うことが出来ました。

ただし、CDKのAPIが良くできていて、Scalaでやるメリットは大きくはなさそうです。(メリットがあるケースはアプリもScalaだから、CDKもScalaで使いたいといった言った程度でしょうか?)

検証コード

今回作った検証コードは こちら

GitHub Actions で AWS CDK

はじめに

AWS Advent Calendar 2019 - Qiita の 10日目への寄稿です。

AWS CDK が 2019年7月11日に、 GitHub Actions が 2019年11月14日にそれぞれ GAと なりました。 qiita.com のようにGitHub へクレデンシャルを登録しておけば、 GitHub Actions を使って CDK によるCloudFormation Stack の構築が出来るのでは無いか?と思い立ったので試してみました。 今回はPythonで記述してみようと思います。

環境構築

GitHub Actions では、Docker コンテナーを使うということで CDK 入りの軽量なコンテナーとなる Dockerfile を github.com 作りました。 そして、CDK を使って CloudFormatuon Stac kを構築するためのコードと、それを動かす GitHub Workflow は GitHub - poad/Advent に置いています。 リポジトリーを分けた方が理解しやすいこともあって分けています。

そもそも Action と Workflow って何?

GitHub Actions では、Action と呼ばれる機能を作成したり、既に用意されている Action を Workflow から呼び出して CI/CD を行います。

Action

後述する Workflow から呼び出して実行するための機能。 Docker コンテナー上で実行される。 action.yml で定義する。

Workflow

前述した Action をパラメーターと共に順に実行していく手順をYAMLで記述したもの。 .github/workflows/main.yml で定義する。

CodePipeline との比較

ざっくりとCodePipeline と比較してみます。

CodePipeline GitHub Actions
GitHub との親和性
AWS との親和性
単発実行 ×

※ 上記評価はあくまでも個人的な主観です

GitHub との親和性

CodePipeline では、GitHubリポジトリーとブランチしか指定出来ません。 一方、GitHub Actions では、リポジトリーとブランチの組み合わせ以外にも、リポジトリーとタグの組み合わせを指定できるほか、 push 以外を起動トリガーと出来ます。

AWS との親和性

当然ながら、CodePipeline であれば CloudFormation や ECS など、様々なサービスのデプロイを既存の Action として指定できます。 また、GitHub Actions はAWSのクレデンシャルを使用して AWS CLIAWS CDK を使用します。 IAM ロールなどを絞りたい場合に専用の IAM ユーザーを作成して絞る必要があります。 一方、CodePipeline では、CodePipeline 自体やそこから呼び出す AWS のサービス向けの IAM ロールを CodePipeline 毎に作成・指定することが出来ます。

単発実行

GitHub Actions では、リポジトリーへの push などを行わないと実行できません。 同じコード、設定で 再デプロイしたい場合に実行する方法がなさそうです。

Action を作ってみる

実は、AWS CDK GitHub Actions · Actions · GitHub Marketplace · GitHub として既に CDK を使うための GitHub Actions があります。 そのまま使用すればいいのですが、それでは記事にならないどころか、必要となった場合に作り方が分からなくなります。

そこで、上記のActionを参考に自分で作ってみます。

Dockerfile

alpine:3npmpython3pipawsclicdk をインストールします。 entrypoint.sh として、CDK のコードで使用する Python module のインストールを行い、 workflow で指定された各サブコマンドを実行するシェルスクリプトを ENTRYPOINT とします。

action.yml

Action の定義を行います。

action.yml のシンタックスMetadata syntax for GitHub Actions - GitHub Help

workflow を作る

.github/workflows/main.yml を作ります。 main.yml 以外に YAML ファイルを置いた場合にどう動くのかは試してません。 先述したとおり、ビルドやデプロイなど、どういった作業の流れなのかを定義したファイルです。 その名の通りですね。

ちなみに、シンタックスWorkflow syntax for GitHub Actions - GitHub Help

step が CodePipeline の Actionのような位置づけで、uses で GitHub Action を指定して with で指定したパラメーターが、 INPUT_プレフィックスが付いて、大文字化された値が環境変数としてActionのDockerコンテナーに渡されます。

なので、例えば cdk_subcommandINPUT_CDK_SUBCOMMAND となります。

今回は、 working_dir で指定した相対パスとなるように cdk init を行っている点がポイントです。

これを、workflow でビルドやデプロイしたいリポジトリーへ push します。

cdk init/CDK を使うコードの作成

ここに関しては、特にこの記事のテーマとしては特記することはありません。
ただ単に、CDK が動く環境で以下のコマンドを実行するだけです。 今回は、 cdk init で作られる CloudFormation Stack が作られれば良いため、特に Python のコードの追加・変更・削除は行っていません。

cdk init --language python

苦労した点

  • GitHub Actions という名前の紛らわしさ
    Action 定義あっての workflow と勘違いしてしまう。。。
    GitHub Workflow なら、やりたい目的を達成するために Action という部品を組み合わせて workflow で処理するんだとわかりやすいと思うのですが、
    個々の Action がメインのようなサービス名なのがイマイチ。
    Action が使いたいのではなくて、ビルドしたりデプロイしたりするのを自動化したいから、目的を達成するのに名前が非直感的かな?と。
    些細なことですが、理解するのに苦労した理由の一つのように思えます。

  • 実は workflow やその中のAction で使うコード(ビルド・デプロイ対象) は clone する step が必要
    actions/checkout@v1 という Action を指定した step があります。
    これは、Action の Docker コンテナーが実行時にマウントする WORKSPACE の場所へ、
    その workflow 定義がコミットされているリポジトリーの内容を clone するためのものです。
    これが必要であると気づくまでが、最も時間を要しました。
    workflow 定義と共に clone されているのですが、そのパスはマウントされないですし、
    Action 定義でもマウント出来るようには出来なくて困りました。
    まぁ、他のリポジトリーのコードがビルド対象です!と言った場合に、確かにその方が要らぬコードが混入しないので正しいかも。

  • Docker のbest practice に従った対応を入れていると動かないことも
    Docker コンテナー内で作業を行うユーザーが root でないと、ファイルアクセスのパーミッションが無くて動きません。 工夫すれば動くのかも知れませんが、流石にそこまでのメリットはなさそう。。。

参考にしたサイト

qiita.com

zshでgit switchが補完されるようにする

前回の記事で、Ubuntuzshをインストールしたはいいが、git switch が補完されずに困っていた。 poad1010.hatenablog.com

調べてみると、こんなIssue が! github.com

どうやら、gitリポジトリのmasterにあるgit-completionを使えば良さそう。

mkdir -p ~/.zsh
curl -o ~/.zsh/git-completion.bash https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash
curl -o ~/.zsh/_git https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.zsh

echo "# Load completion files from the ~/.zsh directory." >> ~/.zshrc
echo "zstyle ':completion:*:*:git:*' script ~/.zsh/git-completion.bash" >> ~/.zshrc
echo "fpath=(~/.zsh $fpath)" >> ~/.zshrc

echo "autoload -Uz compinit && compinit" >> ~/.zshrc

これでzshを立ち上げ直したら、見事!git switch が保管できた!

ちなみに、git switchは、最近、checkout -b ではなく、switch -c を使えだとか、checkoutの代わりにswitchを使えという流れみたいなので、gitをアップデートして、switchコマンドに慣れておいた方がいい。

この辺を読めば、Ubuntu bionicでも最新のgitが使える。 text.baldanders.info

zshでzsh-completions

目的

家の macOS Catalina をようやく、bash から zsh へ移行した。 bashよりも使いやすいため、Dockerコンテナーを使って設定を試し、その手順を書き残そうと思う。

手順

docker pull

docker pull ubuntu:bionic

docker run と最新化

docker run --rm -it --name ubuntu ubuntu:bionic bash
apt update -qq
apt full-upgrade -qqy

zsh と git のインストール

apt install -qqy --no-install-recommends git ca-certificates
apt install -qqy --no-install-recommends zsh

Prezto のインストール

zsh
cd $HOME
git clone --recursive https://github.com/sorin-ionescu/prezto.git "${ZDOTDIR:-$HOME}/.zprezto"
setopt EXTENDED_GLOB
for rcfile in "${ZDOTDIR:-$HOME}"/.zprezto/runcoms/^README.md(.N); do
  ln -s "$rcfile" "${ZDOTDIR:-$HOME}/.${rcfile:t}"
done

これで Prezto のインストールはOK

zsh-completions

git clone git://github.com/zsh-users/zsh-completions.git  "${ZDOTDIR:-$HOME}/.zsh-completions"
echo 'fpath=(${ZDOTDIR:-$HOME}/.zsh-completions/src $fpath)' >> $HOME/.zshrc

zsh-completions のインストールもこれで良い。

というか。。。

公式サイトの手順でaptリポジトリーを追加しようにも、証明書の期限切れ?らしくてエラーとなるオプションで回避出来はするはずだけども、セキュリティー的によくないからやってない。 https://software.opensuse.org/download.html?project=shells%3Azsh-users%3Azsh-completions&package=zsh-completions

echo 'deb http://download.opensuse.org/repositories/shells:/zsh-users:/zsh-completions/xUbuntu_18.04/ /' > /etc/apt/sources.list.d/shells:zsh-users:zsh-completions.list
wget -nv https://download.opensuse.org/repositories/shells:zsh-users:zsh-completions/xUbuntu_18.04/Release.key -O Release.key
apt-key add - < Release.key
apt update -qq
W: GPG error: http://download.opensuse.org/repositories/shells:/zsh-users:/zsh-completions/xUbuntu_18.04  InRelease: The following signatures were invalid: EXPKEYSIG 9CBE063CAB8FE1F1 shells:zsh-users:zsh-completions OBS Project <shells:zsh-users:zsh-completions@build.opensuse.org>
E: The repository 'http://download.opensuse.org/repositories/shells:/zsh-users:/zsh-completions/xUbuntu_18.04  InRelease' is not signed.

bashのcomplationよりも優秀で、↓のように候補が一覧表示されたりする、

$ git c<tab>
 -- common commands --
checkout  -- checkout a branch or paths to the working tree
clone     -- clone a repository into a new directory
commit    -- record changes to the repository

追記

テーマ変更の方法もメモしとく

# テーマ一覧
prompt -l
 
# テーマをプレビュー
prompt -p <テーマ名>
 
# すべてのテーマをプレビュー
prompt -p
 
# テーマを適用(再起動したら戻る)
prompt <テーマ名>

# テーマ適用(.zpreztorc)
zstyle ':prezto:module:prompt' theme '<テーマ名>'

個人的には、元のbashみたいに見える redhat テーマが好き

OpenJDK まとめ(2019年5月6日版)

Oracle JDKの公開アップデート提供が終了したことで色々と騒ぎになっていますが、 サードパーティ製のOpenJDK(OpenJDKのディストリビューション)を使えば大抵は解決するだろう。 楽観的ではなくOracleJava 11をリリースするタイミングで、 JavaFXJava Mission Control/Java Flight RecorderなどをOSS化しているので、 乗り換えれば良いというお話です。

ちなみに、JavaJava SEが有償化したのではなく、 OpenJDKのディストリビューションのひとつである Oracle JDK の公開アップデートが終了した話のまとめは 「Java 有償化」で誤解する人になるべく分かりやすく説明するためのまとめ - Togetter や、なぜ「Javaが有償化する」と誤解されてしまうのか考えてみる - kazokmr's Blog をご覧下さい。

さて、ここからが本題。

数あるOpenJDKのうち、どれを使えばいいの?ということになりそうなので、 OpenJDKのディストリビューションにはどんなものがあるかご紹介します。

※ 筆者はJavaのいちユーザーに過ぎないため、細かい間違いなどはあるかと思いますがご了承ください。

OpenJDK (Oracle公式バイナリ)

OpenJDK

Oracleが中心(あくまでも中心であって、Oracle以外のベンダーも開発に参加しています。新機能の追加は主にOracleらしいです。)に開発されているOpenJDKのOpenJDKプロジェクトによるビルド。 6ヶ月毎にアップデートされて行く。 メジャーバージョンが変わって行くものの、実は今までのOracle JDKでは、 同じメジャーバージョンのアップデートリリースで機能追加が行われていたものが、 きちんと不具合修正のみと機能追加を含むという採番ルールに変わっただけという。。。

JDKの新しいリリース・モデル、および提供ライセンスについて

Red Hat版OpenJDK (この子はだけは無償ではないです)

Red Hat Developer | Red Hat OpenJDK Download

OpenJDKのセキュリティアップデートなどは、JDK 7の頃からRed Hat(最近、RedとHatの間にスペース入るようになったらしい。どうでもいいけど)が中心に行っているそうで、Red Hat Enterprise Linuxと、Windows版が提供されている。

LTSサポートあり。

無償で使えるのはWindows版(おそらくWindows Serverを除く?)だけなので、開発者向けの配布だそうです。

開発者向けの配布で、商用利用はサブスクリプションに加入する必要があるとのこと。

LIBERICA JDK

BellSoft | Java Platform and Applications Experts

BellSoft が提供しているOpenJDKディストリビューション。 LTSサポートあり。 AlpineLinux (must-libc)版あり?(少なくともDockerイメージは提供されています) JavaFX(多分OpenFX)も付属。 Windowsの32bit版もある。

多分、未だJigsaw対応出来ていなくて、Java 8のままなんだけども。。。というプロジェクトや、幾ら何でも6ヶ月毎の更新は対応が間に合わないという型の乗り換え先の最有力候補なんじゃなかろうか?

現状、後述のZuluの方がAlpineLinux対応はしっかりしているらしいです。

Zulu Enterprise

Get Java Support OpenJDK Support subscriptions Java Security Updates

Azul Systemsが提供しているOpenJDKディストリビューション。 LTSサポートあり。 AlpineLinux (must-libc)版あり。 ライセンスはOpenJDKと同じGPLv2+クラスパス例外。 Java 11からはLICENSE テキストファイルが同封されなくなったが、同封のreadme.txtに記載されている。 また、それに加えてZulu/Azul Systemsの免責事項などが記載された利用規約が存在する。(記述内容がGPLv2+クラスパス例外と矛盾する場合はGPLv2+クラスパス例外が優先されるようなことが英語で書かれている)

LIBERICA JDKの次に有力な候補ではないかと思われる。(特にAlpineLinuxを使う場合)

ちなみに、ソースコードは要求しないと貰えないようです。 (昔のインターネット回線が遅かったり、常時接続じゃなかった頃を思い出させられる)

Amazon Corretto

Amazon Corretto(本番環境に対応したOpenJDKディストリビューション)| AWS

Amazon(AWS)が提供しているOpenJDKディストリビューション。 LTSサポートあり。 Windows版、Mac版もある。 基本的にはAmazon Linux向けかな? Javaの父であるJames Goslingは、現在Amazonに所属していることもあって話題となった。 Amazon Linux 2ベースで動いているJavaアプリは、これを使うのが良いのだろう。 (うちのプロジェクトは、AWSに乗っていても、後発のAmazon Linuxに乗り換えてすらいないで 他のOpenJDKディストリビューションに乗り換えましたけどね)

SapMachine

SapMachine | An OpenJDK release maintained and supported by SAP

SAPが提供が提供しているOpenJDKディストリビューション。 LTSサポートあり。 PPCLinux版があるのが特徴。

AdoptOpenJDK

AdoptOpenJDK - Open source, prebuilt OpenJDK binaries

AdoptOpenJDKコミュニティが提供しているOpenJDKディストリビューション。 AdoptOpenJDKプロジェクト自体は、OpenJDKのビルドファームということで、 マイクロソフト含めて様々なベンダーがスポンサーとなっている。 HotSpot VM(Oracle JDKをはじめ、各OpenJDKディストリビューションはこれ)ベースと、 IBMのOpenJ9ベースの両方を提供しているのが特徴。 LTSサポートあり。

ただし、政治的な都合でTCKと呼ばれる互換性確認テストを通せていない。 (AdoptOpenJDKをTCK通したら、他のOpenJDKディストリビューションの存在意義が皆無になるから、 Oracleが嫌がっているという風の噂も。。。噂ですからね)

ちなみに、Windows版にOpenFXをバンドルする予定があるとか。

おまけ

IcedTea

https://icedtea.classpath.org/wiki/Main_Page

Java 8までのRed HatのOpenJDKディストリビューション。 (ページタイトルがMainPageって。。。)

IcedTea-Web

IcedTea-Web - IcedTea

上記IceTeaから分離したJava Web StartOSS実装。 今後、Java Web Startを無償で使うなら、IceTea-Webになるのだろう。

謝辞

OpenJDKソムリエの @yamadamn さん、ご確認およびご指摘いただきましてありがとうございました。