TECH PLAY

miracleave株式会社

miracleave株式会社 の技術ブログ

52

はじめに 新垣です!ここ最近気になっていた js / ts ランタイムのDeno に触れてみたいなぁと思い、 調べているとdeno製のボイラーテンプレートであるdeno blogを発見しました! Minimal boilerplate blogging. All you need is one boilerplate JavaScript file that has 2 lines of code: https://github.com/denoland/deno_blog 今回はこちらの紹介をいたします! 環境情報 MacBook Air (M1, 2020) チップ: Apple M1 OS: MacOS Monterey (バージョン12.4) 環境構築 home brew のインストールは行っている状態になっています。 home brew: 3.5.9 home brew で denoをインストールします。 brew install deno deno をアップグレードします。 deno upgrade deno の version を確認します。私の環境だと以下になりました。 $ deno --version deno 1.24.3 (release, aarch64-apple-darwin) v8 10.4.132.20 typescript 4.7.4 プロジェクトの作成 環境構築を終えて、いよいよプロジェクトを作成します!今回は deno_blog という名前で作成します。 deno run -r --allow-read --allow-write https://deno.land/x/blog/init.ts deno_blog 作成出来たら以下を実行してみましょう。ローカル環境の 8000 ポートでサーバーが立つかと思います。 $ cd deno_blog $ deno task dev 恐竜のアイコンがかわいいですよね! deno deploy を使用すると爆速で公開することもできます!是非使ってみてください! Deploy JavaScript Globally https://deno.com/deploy The post Deno Blog on M1 Mac でブログ構築! first appeared on miracleave Tech Blog .
アバター
はじめに 上野です!最近、個人学習でTerraformを触る機会があったのでM1でのTerraformの実行環境の構築方法を共有しようと思います!今回はDockerを使用してのTerraformの実行環境の構築方法です! 環境情報 MacBook Pro (13-inch, M1, 2020) チップ: Apple M1 OS: MacOS Big Sur (バージョン11.5.2) Docker Engine (v20.10.8) Dockerを使用してTerraformの実行環境を作成する 2022/8/18日現在、hashicorp/terraformのDocker ImageでApple Silicon 対応のものがありません。そのため、自分でTerraform用のDocker Imageを作成する必要があります。ただ、皆さんが思っているほど難しくはないです。たったの4ステップでTerraform用のDocker Imageを作成できます! 1 Terraform用のフォルダとDockerfileを作成 まず最初にターミナルを開き、好きなフォルダ上でTerraform用のフォルダとDockerfileを作成します。 mkdir terraform-sample && touch terraform-sample/Dockerfile && cd terraform-sample 2 DockerfileにTerraform用の設定を記述 先程作成したDockerfileに以下の内容を記述します。 FROM alpine:latest WORKDIR /terraform RUN apk add --no-cache terraform && \ apk add --no-cache git ENTRYPOINT ["terraform"] 3 Docker Imageを作成 Dockerfileが存在するフォルダ上で以下コマンドを実行しDocker Imageを作成します。 docker build -t terraform . 4 コンテナを起動 Terraform用のDocker Imageが出来たら以下コマンドを実行しコンテナを起動します。 docker run -v `pwd`:/terraform -it --rm=true --name=terraform-sample --entrypoint=ash terraform 4ステップ目を完了するとTerraformが動作するコンテナ内に自動で入ります。実際にTerraformが動作するを下記コマンドを実行して確認してみてください。 terraform -v 問題なく動作するようであれば以下のようなメッセージが表示されるはずです。 Terraform v1.2.0 on linux_arm64 Your version of Terraform is out of date! The latest version is 1.2.7. You can update by downloading from https://www.terraform.io/downloads.html 以上がM1 MacでDockerを使用してTerraformを動作させる方法です。 まとめ いかがでしたでしょうか?他にも色々とM1 MacでのTerraformの実行環境を構築する方法の記事がいくつかありますが、こちらの方法はかなりシンプルにTerraformの環境を構築できます。しかも、Dockerを使用しているのでローカルが汚れることもありません。 今回はローカルでのTerraformの実行方法を紹介しましたが、ネット環境されあれば利用できる Terraform Cloud というクラウドサービスもあるので是非こちらも検討してみてください。 今回は以上です! 読んでいただきありがとうございました! The post M1 MacでTerraformの実行環境をDockerで作ってみた first appeared on miracleave Tech Blog .
アバター
はじめに こんにちは!青柳と申します。バックエンドで最近Goを使うことが多くなってきていて、その中でデータを操作するときに便利だったsqlxライブラリをご紹介します。 いろいろ機能はありますが、今回は主にStructにデータを積めるScanの使用方法についてお話しします! sqlxとは Goでよく使用するライブラリとしてはdatabase/sqlという標準ライブラリが挙げられます。基本的にこちらでも問題ないのですが、データを取得したあと、構造体にそのデータを格納してクライアントに返すとき、不便なことがあります。 それらの問題を解決してくれるのがsqlxというライブラリです。こちらはdatabase/sqlの拡張ライブラリとなっています。 sql package - database/sql - Go Packages Package sql provides a generic interface around SQL (or SQL-like) databases. pkg.go.dev sqlx package - github.com/jmoiron/sqlx - Go Packages Package sqlx provides general purpose extensions to database/sql. pkg.go.dev sqlで実装した場合 package main import ( "fmt" "log" _ "github.com/go-sql-driver/mysql" "database/sqlx" ) // Structを用意 type User struct { ID int `json: "id"` Name string `json: "name"` Email int `json: "age"` Tel string `json: "tel"` } func main() { //DBに接続する db, err := sql.Open("mysql", "root/sample") if err != nil { defer db.Close() log.Fatal(err) } // SQL実行 rows, err := db.Query("SELECT id, name, email, tel FROM users") if err != nil { log.Fatal(err) } defer rows.Close() results := make([]User, 0) for rows.Next() { var user User err := rows.Scan( &user.ID, &user.Name, &user.Email, &user.Tel ) if err != nil { log.Fatal(err) } results = append(results, user) } fmt.Println(results) } 特に注目していただきたいのは err := rows.Scan( &user.ID, &user.Name, &user.Email, &user.Tel ) の部分です。sqlのScanは下記のような特性を持ちます。 データと同じ個数の引数が必要 データの当てはめは順番に依存する。(例えば、上記SQLのSELECT句がid, name, email, telとなっているので、Scanの引数の順番も必ず、上記の通りじゃないといけない。&user.Name、&user.ID、、、のように順番を入れ替えるとエラーまたは適切に値を取得できない) そのため、 データが増えたとき、その分Scanの引数を書く必要があるため、冗長になりやすい。 動的にデータの個数が変わる場合、それに合わせたScanを用意する必要がある。(データ個数が3つの場合は引数が3個のScan、10個の場合は引数が10個のScanを用意する) 順番が不正でもStructの型が同じだとエラーにならないのでバグになりやすい。 という弱点を持ちます。これを解決してくれるのがsqlxライブラリです。 sqlxで実装した場合 上記のsqlを使用して書いたソースコードをsqlxで書き直してみましょう。 package main import ( "fmt" "log" _ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" ) // Structを用意 type User struct { ID int `db: "id" json: "id"` Name string `db: "name" json: "name"` Email int `db: "age" json: "age"` Tel string `db: "tel" json: "tel"` } func main() { //DBに接続する db, err := sqlx.Open("mysql", "root/sample") if err != nil { defer db.Close() log.Fatal(err) } // SQL実行 rows, err := db.Queryx("SELECT id, name, email, tel FROM users") if err != nil { log.Fatal(err) } defer rows.Close() results := make([]User, 0) for rows.Next() { var user User err := rows.StructScan(&user) if err != nil { log.Fatal(err) } results = append(results, user) } fmt.Println(results) } 変更点を見ていきましょう。 “github.com/jmoiron/sqlx”はライブラリを読み込むために必要なので記載してください。 type User struct { ID int `db: "id" json: "id"` Name string `db: "name" json: "name"` Email int `db: "age" json: "age"` Tel string `db: "tel" json: "tel"` } Struct部分は json: “id” から db: “id” json: “id” のように変更します。 db: とすることでsqlxのStructScanがStructを読み込むことができるようになります。 また、 json: “id” を残しているのは、レスポンスのオブジェクトのkeyを ID ではなく id で返したいからです。 ID int `db: “id”` としても機能するのですが、この場合、レスポンスが { ID: 1 } のようになります。もし、 { id: 1 } のようにデータベースのカラムと同じ形で返したい場合は db: “id” json: “id” のようにしてください。これはデータを受け取るクライアントの要件によると思いますので都度検討していただければと思います。 rows, err := db.Queryx("SELECT id, name, email, tel FROM users") sqlを使用していたときは Query でしたが、sqlxでは Queryx というメソッドを使用します。他にも多数独自のメソッドがありますので詳しくは上記リンクのドキュメントをご覧ください。 err := rows.StructScan(&user) 最後にStructへデータを当てはめるScanですが上記のようにStructScanを使用します。その際、sqlではデータの数だけ引数に &struct.プロパティ を用意する必要がありましたが、sqlxのStructScanではStructのポインタを渡すだけで完結します。これで自動的にデータを投入してくれるので、動的にデータの個数が変わる場合も対応できますし、順番を気にする必要性もなくて非常に便利です。 まとめ いかがでしたでしょうか?標準ライブラリをそのまま使用しても問題はありませんが拡張されたsqlxを使用することで弱点を補うことができます。また、拡張ライブラリなので(あまりお勧めしませんが)sqlとsqlxを同時に使用することも場合によっては可能です。 上記以外にもいろいろな拡張機能があるので是非お試しください。 読んでいただきありがとうございました! The post GOのデータ操作に便利なsqlx first appeared on miracleave Tech Blog .
アバター
こんにちは、竹村です! 先月、弊社で実施したmirameet「現場で役立つ実践Vue.js!~ECサイトを作成してみる~」では触れなかった、Dockerを用いての環境構築部分について、備忘録も兼ねて書いていきたいと思います。 フォルダ構成 フォルダ構成は以下のように作りました。 バックエンド(server)とフロントエンド(client)でフォルダを分けています。 docker-compose.yml Python環境とVue.js環境を別サーバーで構築するので、docker-compose.ymlを準備します。 バックエンドをserver、フロントエンドをclientとしています。 ・docker-compose.yml version: '3.8' services: server: build: context: ./server dockerfile: Dockerfile volumes: - './server:/usr/src' ports: - "8000:8000" tty: true stdin_open: true client: build: context: ./client dockerfile: Dockerfile volumes: - './client/mysite:/usr/src' ports: - "8080:8080" tty: true stdin_open: true depends_on: - server environment: - NODE_ENV=development client側の設定で、依存関係を「depends_on」にて明示します。 また、コンテナ起動を持続させるために「tty: true」を設定しています。 Dockerfile(server, client) server用、client用のDockerfileをそれぞれ準備します。 それぞれのWORKDIRには、docker-compose.ymlのvolumesで設定した「/usr/src」を指定しています。 ・Dockerfile(server用) # pull official base image FROM python:3.9-slim-buster WORKDIR /usr/src # set working directory ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 # install system dependencies RUN apt-get update \ && apt-get clean # install dependencies RUN pip install --upgrade pip COPY ./requirements.txt . RUN pip install -r requirements.txt # add app COPY . . ・Dockerfile(client用) # pull official base image FROM node:16-alpine WORKDIR /usr/src ENV HOST 0.0.0.0 EXPOSE 8080 requirements.txt Python環境構築時、pipインストールさせるパッケージをrequirements.txtに記載します。 ・requirements.txt asgiref==3.5.0 Django==3.2.12 djangorestframework==3.13.1 django-cors-headers==3.11.0 django-filter==2.4.0 pytz==2021.3 sqlparse==0.4.2 コンテナ起動 必要なファイルを用意したら、以下コマンドでビルドし、コンテナを起動します。 docker-compose up -d --build バックエンド:Djangoアプリケーションを作成する コンテナが起動したら、Djangoアプリケーションを作成する為にserverコンテナに入ります。 docker-compose exec server bash コンテナに入ったら、以下順番でコマンドを打ちこみます。 マイグレーションまで一気に実施します。 django-admin startproject config . python manage.py startapp api python manage.py make migrations python manage.py migrate マイグレーションが終わったら、サーバーを起動します。 python manage.py runserver 0.0.0.0:8000 http://localhost:8000/ にアクセスし、Djangoのスタート画面が表示されていればOKです。 フロントエンド:Vue.jsでアプリケーションを作成する serverコンテナに入ったままなのでexitコマンドで抜けるか、新たなターミナルを立ち上げてください。 以下コマンドでclientコンテナに入ります。 docker-compose exec client sh 続いて、Vue CLIをインストールします。 npm install -g @vue/cli Vueアプリケーションを作成します。 ホスト側におけるmysite直下に作成したいため、カレントディレクトリで指定します。 vue create . コマンド実行後、カレントディレクトリに作成するかどうか聞かれるので、「y」を選択します。 インストールするVueのバージョンは、特に理由が無ければ3系を選択して大丈夫です。 私は、現在の現場で2系を使用していることもあり、2系でインストールしました。 Vueアプリケーションが作成されたら、サーバーを起動してみましょう。 npm run serve http://localhost:8080/ にアクセスし、起動されているか確認します。 終わりに これで、バックエンド、フロントエンドのサーバーが立ち上がりました! ここまで環境を構築した後は、docker-compose.ymlにサーバー起動のcommandを設定しておけば、 コンテナ起動時に立ち上がるので楽です。(フロントサーバーは起動に少し時間がかかります) 今回はサーバー起動編ということで、次回はCORS認証やDjangoのsetting.pyの設定等について書ければと思います。 ではまた次回のテックブログでお会いしましょう! The post DockerでPython×Django×Vue.jsの環境を構築する(サーバー起動編) first appeared on miracleave Tech Blog .
アバター
みなさんお疲れ様です!佐々木です! 最近は何かとキャッチアップすることが多いのでやることなすこと中途半端になって、悩みのタネが付きません!若いときより記憶力もおちているなーと地味に落ち込みます、、 ですがそうも言ってられないので覚えるまでやるマインドで今後ともがんばります笑 AWS IAMのお話です。 AWSといったら様々なサービスが沢山ありますね!基本的なところだとEC2やS3とかRDS・DynamoDB、コンテナだとECS、EKS、サーバレスならlambda、Amplify…… はい!たくさんです!(白目)ですが、今日お話ししたいのはIAMです。 なんとなく地味なイメージの人もいるかも知れませんが、ルートユーザーで環境ぶん回してるような人は一度落ち着いてください。ほんとにどえらいことになるので。 IAM(Identity and Access Management)とは AWSのサービスの一つであり、「認証」と「認可」の設定を行うことができるサービスで、「認証」「認可」を正しく設定することで、AWSの利用者や、AWSのサービスがアクセスできる範囲を制御することができます。 ここでいう「利用者」や「サービス」とは後述の「プリンシパル」を指します。 IAM自体の詳細は、以下の公式ドキュメントや解説を参考にしてください。 AWS IAM(ユーザーアクセスと暗号化キーの管理)| AWS AWS Identity and Access Management (IAM) を使用して、AWS のサービスやリソースへのアクセスを安全に管理します。 aws.amazon.com IAMユーザー IAMユーザーとは、ルートユーザーにより、AWSアカウントの範囲内で権限を与えられた個別のユーザーを指す。これはプログラムやアプリケーションの場合もあります。 ユーザーはアクセスキーかパスワード、あるいは両方を選択して認証する方式を決定します。 アクセスキーについてはアクセスキーIDとシークレットアクセスキーが発行されます。 IAMポリシー IAMポリシーとは、AWSリソースに対する許可・拒否を示すもの。 また、認証主体(プリンシパル)として評価される、IAMユーザーやIAMロールにアタッチして使用することが出来ます。(厳密にはIdentityにアタッチ) ポリシーはjson形式で記載されており、設定項目の詳細としては以下の通りです。 プリンシパルを指定・・・Principal サービスとアクションを指定・・・Action リソースを指定・・・Resource コンディション(ポリシーの条件)を指定・・・Condition なお、ポリシーにはアイデンティティベースとリソースベースの2種類があり、アイデンティティベースのポリシーは認証主体そのものにアタッチするもの、リソースベースのポリシーは操作されるAWSリソース側にアタッチするものになります。 IAMロール IAMロールとはポリシーを複数アタッチすることが出来る任意の権限をまとめたものであり、特徴として信頼関係と権限という2つの性質を持っています。 ロールにスイッチ出来るポリシーを持ったIAMグループやユーザーであればロールにスイッチすることが出来ます。 IAMのここは抑えときましょう アイデンティティベースポリシーとリソースベースポリシーについてはきちんと理解しておきたい。 前者は、 「誰がどのリソースに対してどんなアクションを実行できるか」 というものです。 例えば、 「devuser」が「EC2インスタンス」に「参照、スタートとストップ」アクションを行う。 という権限を与えようとした場合、「EC2インスタンスの参照、スタートとストップ」に見合ったポリシーを作成し、それを「devuser」 にアタッチすることで適用されます。 後者は 「このリソースに対して誰がどんなアクションを実行できるか」 です。 これを指定して作成したポリシーをリソースにアタッチすることで、指定した対象(プリンシパル)に 対してアクセスを許可する。といったものになります。 クロスアカウントアクセスのやり方 ■やること 主アカウント(A)で副アカウント(B)のリソースを扱えるようにする。 ■準備すること ①既存のAWSアカウントとは別にもう1アカウント作成する。また、Administratorのアカウントを作成しておく。 ②主アカウント(A)のアカウントIDをメモしておく。(すぐ確認できるようにしておけばOK) ■手順 1.副アカウント(B)のAdministratorのアカウントでログインして、コンソールからIAMを選びます。 2.IAMの画面に移ったら、ロールの作成を選択します。 3.次に操作する側(主アカウント(A))のアカウントIDを入力します。また、MFAを使用している方はチェックを忘れずに。 4.次にアタッチするポリシーを選択。ここでは「AdministratorAccess」を選択。(こちらは本来はもっと弱い権限でおこなうのが一般的なので実務ではやらないようにしましょう) 5.次のタグはご自由にしていただいて結構です。最後に任意のロール名をつけたら、ロールの作成を押します。 6.ロールが作成される。(自分はhostAccessという名前にしました) 7.以下の画像にある「ロールARN」ですが、この部分を次に使用するので何かしらで控えておきます。 8.ポリシーを作成。(以下のコードのうち、「ここにコピペしてね」と書いてある部分に先程の「ロールARN」に記載されている文字をコピペして下さい。) { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "sts:AssumeRole" ], "Resource": [ "ここにコピペしてね" ] } ] } 9.ポリシーの作成が終わったら、ロールにアタッチします。ここまでで、副アカウント(B)で必要なことは終わりです。(副アカウント(B)のアカウントIDと作成したロール名は控えておいて下さい。) 10.主アカウント(A)のマネジメントコンソール部で主アカウント(A)の名前部をクリックすると、「ロールの切り替え」というのが出るので押す。(オレンジ塗り潰しの箇所です) 11.以下画面の Switch Role ボタンを押します。 12.この画面では、操作する相手先の(ここでは副アカウント(B))のIDとロール名を入力して、「ロールの切り替え」を押す。 13.これで、 主アカウント(A) が副アカウント(B)を操作出来るようになりました。 アカウントの名前を押すとこのようになっていると思います。元のアカウントに戻るときも「〜〜に戻る」と書いてあるので楽チンですね。 ※初期のままだと、ロールの最大セッション時間が1時間になっています。副アカウント(B)の方で作成したロールのアクセス権限を編集する画面で「1時間〜12時間」の範囲で選択出来ます。 おわりに 地味な内容だったかもしれませんが、僕はAWSのサービスの中で一番好きなサービスです。 みなさんも自分でアプリ作ってデプロイしたり、インフラ構築するときのためにもIAMはしっかり抑えておきたいところですね! みなさんがIAMと仲良くなれますように!ではこのへんで! The post AWS IAMの知見を深めてクロスアカウント設定をしてみる話 first appeared on miracleave Tech Blog .
アバター
お久しぶりです!新垣です! 最近 Typescript の勉強を始めたので簡単に実行できる環境ほしかったのと Jestも実行できるようにしたかったので Dockerで実行環境を作ってみたので共有します! 以下記事を参考にさせていただきました。 Node(typescript) + docker 環境構築メモ https://zenn.dev/gakin/scraps/4cc16e7761d1ef JestをTypeScriptで使う https://zenn.dev/kohski/articles/typescript_jest 1.Dockerfile & docker-compose.yaml の追加 以下コマンドでファイルの追加とdockerの設定の追加 touch Dockerfile docker-compose.yaml Dockerfile FROM node:12 WORKDIR /app RUN npm install && \ npm init -y && \ yarn add --dev typescript ts-node ts-node-dev jest ts-jest @types/jest && \ mkdir src tests && touch src/app.ts tests/index.test.ts && \ cd /usr/local/bin &&\ ln -s /app/node_modules/typescript/bin/tsc && \ tsc --init docker-compose.yaml version: '3' services: app: build: context: . dockerfile: Dockerfile tty: true volumes: - .:/app 2. 実行ファイルを作成 $ mkdir src tests && touch src/index.ts tests/index.test.ts src/index.ts const message = 'hello world!' console.log(message) export default message tests/index.test.ts import message from '../src' test('check message value', () => { expect(message).toBe('hello world!') }) 3. 設定ファイルの追加と修正 $ touch package.json package.json { "name": "app", "version": "1.0.0", "description": "", "main": "index.js", "dependencies": { "ts-node": "^10.8.1", "typescript": "^4.7.4", "tsc": "2.0.4" }, "devDependencies": { "@types/jest": "^28.1.3", "jest": "^28.1.1", "ts-jest": "^28.0.5" }, "scripts": { "test": "jest --coverage" }, "author": "", "license": "ISC" } 4. Dockerの起動 $ docker-compose build $ docker-compose up -d 5. jest の設定 jest.config.js /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { preset: 'ts-jest', testEnvironment: 'node', roots: ["<rootDir>/tests", "<rootDir>/src"], collectCoverage: true, collectCoverageFrom: [ "*/*.ts", "!**/node_modules/**", ], coverageDirectory: 'coverage_dir', coverageReporters: ["html"] }; 5. ファイルのコンパイルと実行 tsファイルのコンパイル $ docker-compose exec app tsc src/index.ts js ファイルの実行 $ docker-compose exec app node src/index.js jest 実行 $ docker-compose exec app npm test 皆さんがさくっと環境構築できて Typescript の学習に集中できると私もうれしいです! みんなでTypescriptマスターを目指しましょう! The post Typescript × Jest × Docker の実行環境をさくっと構築! first appeared on miracleave Tech Blog .
アバター
はじめに 上野です! 以前に「 VSCodeでGolang✖︎Next.jsの開発環境を構築してみた 」という記事を投稿したのですが、記事の内容通りの環境だと色々と不便なことが出てきたので、改めて開発環境を見直してみました。 特に不便だと感じたのが、GolangとNext.jsのコンテナが別々になっていることによって、VSCodeのWindowを二つ開かないといけないのが特に不便に感じました。どうせなら、一つのWindowでソースコードの編集できれば楽だということに気づきました… ということで、今回は一つのコンテナ上でGolangとNode.jsが動作する環境を作成し、そのコンテナにVSCodeで接続して開発ができるようにしていきたいと思います! GolangとNode.jsが動作するDockerfileを作成 フォルダ構成は以前のを使用します。 以前の「 VSCodeでGolang✖︎Next.jsの開発環境を構築してみた 」の記事を参考にGolang✖︎Next.jsのフォルダを作成してください。 新しく追加したファイルに関しては「(New)」と記載しています。 go-next |_.devcontainer | |_devcontainer.json (New) | |_.vscode | |_launch.json (New) | |_server | |_.devcontainer | | |_devcontainer.json | |_.vscode | | |_launch.json | |_Dockerfile | |_.air.toml | |_main.go | |_client | |_.devcontainer | | |_devcontainer.json | |_Dockerfile | |_docker-compose.yml | |_Dockerfile.dev (New) では、最初にDockerfile.devのファイルを作成します。 # ------------------------ Golang --------------------------- FROM golang:1.17-alpine AS golang # 環境変数設定 ENV ROOT=/go/src/app WORKDIR ${ROOT} RUN apk update && apk add git RUN go install golang.org/x/tools/cmd/goimports@latest \ && go install golang.org/x/tools/gopls@latest \ && go install golang.org/x/tools/cmd/godoc@latest \ && go install golang.org/x/lint/golint@latest \ && go install github.com/rogpeppe/godef@latest \ && go install github.com/nsf/gocode@latest \ # hot relord && go install github.com/cosmtrek/air@latest \ # debug && go install github.com/go-delve/delve/cmd/dlv@latest ADD ./server/shell/mysqldef.sh ${ROOT}/shell/mysqldef.sh RUN sh ${ROOT}/shell/mysqldef.sh # GO MODULEインストール COPY ./server/go.mod . COPY ./server/go.sum . RUN go mod download # # ------------------------ Node --------------------------- FROM node:17-alpine as node # # ------------------------ Develop --------------------------- FROM alpine as dev # 環境変数設定(GO) ENV GOPATH=/root/go ENV PATH $PATH:/usr/local/go/bin/:/usr/local/go/bin/go:/go/bin ENV GO111MODULE=on WORKDIR /app RUN apk update && \ apk add --no-cache && \ apk add curl && \ apk add tzdata && \ apk add git && \ apk add openssh && \ apk add make # Golang用 COPY --from=golang /usr/local/bin /usr/local/bin COPY --from=golang /usr/local/go /usr/local/go COPY --from=golang /go/bin /go/bin COPY --from=golang /go/pkg ${GOPATH}/pkg # Node.js用 COPY --from=node /usr/local/bin /usr/local/bin COPY --from=node /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/npm COPY --from=node /opt/yarn* /opt/yarn RUN ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn && \ ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg # Gin Port EXPOSE 8080 # Next.js Port EXPOSE 3000 こちらのDockerifleでは、マルチステージビルドを利用しています。 GolangとNode.jsのステージから必要なフォルダ抽出しalpineステージにコピーしています。 次に.devcontainer/devcontainer.jsonを作成します。 { "name": "Go devcontainer", "build": { "dockerfile": "../Dockerfile.dev", "target": "dev" }, "mounts": [ "source=${localWorkspaceFolder},target=/app,type=bind,consistency=cached" ], "workspaceFolder": "/app", "appPort": ["7777:8080", "3434:3000"], "settings": { "editor.tabSize": 2, "editor.formatOnPaste": true, "editor.formatOnType": true, "editor.renderWhitespace": "all", "editor.bracketPairColorization.enabled": true, "editor.guides.bracketPairs": "active", "files.trimTrailingWhitespace": true, "files.trimFinalNewlines": true, "go.toolsManagement.checkForUpdates": "off", "go.inferGopath": true, "go.useLanguageServer": true, "eslint.packageManager": "yarn", "eslint.run": "onSave", "eslint.nodePath": "/app/client", "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "[go]": { "editor.defaultFormatter": "golang.go", "editor.insertSpaces": true, "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true }, "editor.suggest.snippetsPreventQuickSuggestions": false }, "[javascript]": { "editor.formatOnSave": true }, "[typescript]": { "editor.formatOnSave": true }, "eslint.validate": [ "javascript", "javascriptreact", "typescript", "typescriptreact" ] }, "extensions": [ "golang.go", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", ], "shutdownAction": "none" } devcontainerファイルは以前のGolangとNode.jsのdevcontainerファイルを合体させたものになります。 異なっている点としては、docker-composeファイルの指定からDockerfileの指定になっている点です。 Golangでデバックができるよう.vscode/launch.jsonファイルを作成します。 { "version": "0.2.0", "configurations": [ { "name": "Go", "type": "go", "debugAdapter": "dlv-dap", "request": "attach", "mode": "remote", "cwd": "${workspaceFolder}/server", "port": 2345, "host": "0.0.0.0", "showLog": true, } ] } 以上でコンテナ一つで開発ができる環境ができました。 開発する際は、VSCodeの一番左下のボタンから「Reopen in Container」を選択し接続してみてください。 まとめ 以前の開発環境だとわざわざ二つのwindowを開かないといけず、gitの操作も以前の環境だとできませんでした。ただ、今回の一つのコンテナに接続して開発する方法だと一つのwindowを開くだけで済み、かつgitの操作もコンテナ内で行えるようになりました。今後はこの開発環境をベースにGolangとNext.jsのアプリを作ってみたいと思います。 今回は以上となります。ありがとうございました! The post Golang✖︎Next.jsの開発環境を見直してみた first appeared on miracleave Tech Blog .
アバター
はじめに こんにちは!最近エスプレッソにハマってお金の出費が止まらない青柳です!(誰か止めて、、、) 今回はReactの認証で使用するJWT通信の実装についてご紹介していきます! JWTの仕組みについては他サイトに頼りますが、認証を行う上で、クライアント側はaccess_tokenを送る必要があります。また、それが無効になっていた場合、refresh_tokenを使用して、新たに有効なaccess_tokenを取得し直すことも必要です。 今回はこれをどのようなタイミングで、どのように実装すれば良いのかということにフォーカスを当てて解説していきます! 前提 access_tokenはstateに保持します。(LocalStorageやCookieに保存するケースもあると思いますが今回はStateにします) refresh_tokenはAPIからHttpOnly属性を付与してCookieに保持するように送ります。 今回はAPIの実装は割愛します。 構成 今回は簡単に下記のような構成で実装してみます。 src ├ api │ └ axios.js // axiosをimportしてカスタマイズ │ ├ contexts │ └ Auth.jsx. // 認証状態を管理するためのContext │ ├ hooks │ └ useAuthAxios.js. // axios.jsでカスタマイズしたaxiosをimportして認証をする │ └ useRefreshToken.js. // refresh_tokenを扱う │ ├ App.jsx └ text.jsx // APIを呼び出してテストするjsxファイル ソース解説 では早速ソースの解説をしていきます! // axios.js import axios from "axios"; const BASE_URL = "http://localhost:3500"; export default axios.create({ baseURL: BASE_URL }); export const authAxios = axios.create({ baseURL: BASE_URL, headers: { "Content-Type": "application/json" }, withCredentials: true, }); こちらはライブラリのaxiosをimportして、カスタマイズしています。実際に使用する時は BASE_URL (APIのURL)を変更して使用してください。 主に使用するのはauthAxiosの方で、これは認証情報をAPI側に送る設定をしています。 // Auth.jsx import React, { createContext, useState } from "react"; export const AuthContext = createContext({}); const AuthProvider = ({ children }) => { const [auth, setAuth] = useState({}); return ( <AuthContext.Provider value={{ auth, setAuth }}> {children} </AuthContext.Provider> ); }; export default AuthProvider; これはContextでauth stateに認証情報を保持します。 // App.jsx import { BrowserRouter as Router, Route, Routes, Navigate, } from "react-router-dom"; import AuthProvider from "./contexts/Auth"; import Test from "./test"; const App = () => { return ( <Router> <Routes> <Route path="/"> <Route path="login" element={<Login />} /> <Route path="404" element={<NotFound />} /> <Route path="*" element={<Navigate to="404" replace />} /> </Route> {/* ▼ ログイン後ルーティング ▼ */} <Route path="/app/*"> <AuthProvider> <Route path="test" element={<Test />} /> </AuthProvider> </Route> </Routes> </Router> ); }; export default App; こちらがルーティングになります。今回ログインはさわりませんがログインすると/appにアクセスができるようになります。そのため、今回使用するtest.jsxのTestコンポーネントはAuthContextの認証情報を参照することができます。 // useRefreshToken.js import axios from "../api/axios"; import { useContext } from "react"; import { AuthContext } from "../contexts/Auth"; const useRefreshToken = () => { const { setAuth } = useContext(AuthContext); const refresh = async () => { // cookieに保存されたrefresh_tokenを送付してaccess_tokenを取得する const response = await axios.get("/refresh", { withCredentials: true, }); setAuth((prev) => { // access_tokenを保持する return { ...prev, accessToken: response.data.accessToken }; }); return response.data.accessToken; }; return refresh; }; export default useRefreshToken; このファイルではrefresh_tokenを元にaccess_tokenを取得するロジックが定義されています。/refreshというエンドポイントに対して withCredentials: true とすることで、Cookieに保存したrefresh_tokenを送付します。それを元にAPIがaccess_tokenを返すので、 AuthContextのsetAuth に保持するのと同時に、 return response.data.accessToken として、値を返します。 //useAuthAxios.js import { authAxios } from "../api/axios"; import { useContext, useEffect } from "react"; import useRefreshToken from "./useRefreshToken"; import { AuthContext } from "../contexts/Auth"; const useAuthAxios = () => { const refresh = useRefreshToken(); const { auth } = useContext(AuthContext); useEffect(() => { // リクエスト前に実行。headerに認証情報を付与する const requestIntercept = authAxios.interceptors.request.use( (config) => { if (!config.headers["Authorization"]) { config.headers["Authorization"] = `Bearer ${auth?.accessToken}`; } return config; }, (error) => Promise.reject(error) ); // レスポンスを受け取った直後に実行。もし認証エラーだった場合、再度リクエストする。 const responseIntercept = authAxios.interceptors.response.use( (response) => response, async (error) => { const prevRequest = error?.config; // 403認証エラー(headerにaccess_tokenがない。もしくはaccess_tokenが無効) if (error?.response?.status === 403 && !prevRequest.sent) { prevRequest.sent = true; // 新しくaccess_tokenを発行する const newAccessToken = await refresh(); prevRequest.headers["Authorization"] = `Bearer ${newAccessToken}`; // 再度実行する return authAxios(prevRequest); } return Promise.reject(error); } ); return () => { // 離脱するときにejectする authAxios.interceptors.request.eject(requestIntercept); authAxios.interceptors.response.eject(responseIntercept); }; }, [auth, refresh]); return authAxios; }; export default useAuthAxios; 今回の最重要のファイルです!このカスタムフックでは axios.js で作成した authAxios にリクエスト直前とレスポンス直後に実行した処理を追加して返す関数になっています。 ではまずはリクエスト直前の処理を見ていきましょう。 const requestIntercept = authAxios.interceptors.request.use( (config) => { if (!config.headers["Authorization"]) { config.headers["Authorization"] = `Bearer ${auth?.accessToken}`; } return config; }, (error) => Promise.reject(error) ); axios.js からimportしてきた authAxios に対して interceptors メソッドを実行しています。これは、 thenまたはcatchによって処理がされる前にリクエストまたはレスポンスを挟み込む (まさにインターセプト)ことができるメソッドです。 上記では interceptors.request としているのでリクエストが実行される前に処理が行われることになります。ここの処理ではhttpヘッダーのAuthorizationにAuthContextに保持しているaccess_tokenを設定します。これが正常に行われるとAPI側でaccess_tokenを検知して認証することができるようになります。既にAuthorizationにaccess_tokenがある場合はそのままconfigを返します。 では続いてレスポンス後の処理を見てみましょう。 const responseIntercept = authAxios.interceptors.response.use( (response) => response, async (error) => { const prevRequest = error?.config; // 403認証エラー(headerにaccess_tokenがない。もしくはaccess_tokenが無効) if (error?.response?.status === 403 && !prevRequest.sent) { prevRequest.sent = true; // 新しくaccess_tokenを発行する const newAccessToken = await refresh(); prevRequest.headers["Authorization"] = `Bearer ${newAccessToken}`; // 再度実行する return authAxios(prevRequest); } return Promise.reject(error); } ); こちらは interceptors.response としているためレスポンスの直後に実行します。何事もなければ通常通りレスポンスを返します。もしaccess_tokenのセッション切れ等によってAPIで403エラーを受け取った場合(APIをそのような作りにしている前提)、新たにaccess_tokenを取得します。 const newAccessToken = await refresh(); のrefreshはuseRefreshToken フックの関数で新しいaccess_tokenを返してくれます。受け取った newAccessToken を prevRequest.headers[“Authorization”] = `Bearer ${newAccessToken}`; のように再度ヘッダーに詰め直して、最後に return authAxios(prevRequest) で再度リクエストを実行します。 すると今度は正常なaccess_tokenのため認証が通り、正常にレスポンスが変えるという仕組みです。 // test.jsx import { useState, useEffect } from "react"; import useAuthAxios from "./hooks/useAuthAxios"; const Test = () => { const [value, setValue] = useState(); const authAxios = useAuthAxios(); useEffect(() => { const getUsers = async () => { try { const response = await authAxios.get("/users"); setValue(response.data); } catch (error) { console.error(error); } }; getUsers(); }, []); return <>{value}</>; }; export default Test; 最後に実際にアクセスしてその結果を出力します。 /users はユーザーの情報を取得するAPIです。このAPIは認証されていないと情報を取得することができません。そのため先ほど設定した authAxios を使用します。 authAxios を使用すれば、もし認証が切れていても自動的にaccess_tokenを更新してくれて正常にレスポンスが返ってくるので、UI側では特に認証を意識せずに実装することができます。(例外処理等は必要です) まとめ いかがでしたでしょうか?JWTを使用するとどのタイミングでaccess_tokenの更新をすればいいのか悩んだり、どのように実装すればいいかわからないことも多いかと思います。以前私はこの axios.interceptors を知らずに自分でゴリゴリ実装したことがありますが、可読性も悪くあまり納得できるものは作れませんでした。是非こちらを活用して認証周りを整えてみてください。 Interceptors | Axios Docs axios-http.com The post React × JWT認証にはaxiosのInterceptors first appeared on miracleave Tech Blog .
アバター
はじめに こんにちは!竹村です。そろそろTシャツで外出できる季節になってきて、心なしかテンションが上がっております。 さて、以前テックブログにて「jsPDFを使ってKonvaのstage要素をPDF化する際に困った話」というタイトルで記事を投稿しましたが、今回は、前回の内容から派生させ、生成したPDFをバイナリデータ化してDBに保存する方法をご紹介します! jsPDFを使ってKonvaのstage要素をPDF化する際に困った話 こんにちは!話題のELDEN RINGを買ったはいいけどあまり遊べていない竹村です。現場業務にて、JavaScriptのフレームワークであるKonva.jsとjsPDFを使って、画面に描画されているstage要素をPDF化しようと... www.miracleave.co.jp 2022.04.05 blob型で出力したバイナリデータをbase64に変換する PDFインスタンスに対してaddImageした後、outputメソッドの引数に”blob”を渡し、バイナリデータをblob型で生成します。 function convertPdf(self) { var stage = self.$refs["stage"]; var getStage = stage.getStage(); var width = getStage.attrs.width; var height = getStage.attrs.height; //pdfインスタンスを生成 var pdf = new jsPDF({ orientation: "l", unit: "px" format: [width, height], compress: true, }); var scale = getStage.getScale(); var position = getStage.getPosition(); getStage.setScale({ x: 1, y: 1 }); getStage.setPosition({ x: 0, y: 0 }); //pdf化処理 var output = getStage.toImage({ callback(image) { //pdfインスタンスにimageを追加 pdf.addImage(image, 10, 10, width, height); getStage.setScale(scale); getStage.setPosition(position); //pdfインスタンスからblob生成 var blob = pdf.output("blob"); //FileReaderオブジェクトを生成し、blobデータを読み込む var reader = new FileReader(); reader.readAsDataURL(blob); //blobデータ読込完了後、保存API呼び出し reader.onload = function () { savePdfData(self, reader.result); }; }, mimeType: "image/png", x: 10, y: 10, width: width, height: height, }); } //保存API async function savePdfData(self, result) { const response = await self.$postAjax("保存処理APIのURL", result); } 今回はblobで出力しましたが、arraybuffer等の他の形式でも出力できます。 詳しくは公式ドキュメントをご参照下さい。 jsPDF - Documentation artskydj.github.io blobデータをFileReaderのreadAsDataURLで読み込むことで、FileReaderのresult属性に base64エンコーディングされた文字列が格納されます。 読み込みが完了した後、reader.onloadが発火し、保存APIにreader.resultを渡しています。 終わりに いかがでしたでしょうか?手軽にPDFをダウンロードできるjsPDFですが、DBにバイナリデータを保存する事でPDFを扱える幅が広がりますね! リッチな公式ドキュメントも用意されていますし、実際の開発現場でも十分に導入できるライブラリだと思います。 ではまたお会いしましょう! The post 【jsPDF】生成したPDF情報をDBに保存する! first appeared on miracleave Tech Blog .
アバター
みなさん、こんにちは! 次の祝日は7月18日という事実と向き合えず、だいぶ落ち込んでる佐々木です! さて、みなさんは今年のGWで何かしら新しい学びはありましたか? 僕はまとまった休みということもあり、「GraphQL」に入門してみました!(今更!) 今回は、お世話になった本も含め、読みすすめていく過程で、つまづいたりしたところを紹介できればなと思います! GraphQL GraphQL は、API の速度、柔軟性、開発者にとっての使いやすさを向上させるために設計された、API向けのクエリ言語・ランタイムです。 特徴としては、以下のようなものが挙げられます。 クライアントがリクエストした「必要なデータのみ」を取得することができる。 1回のリクエストで必要なデータを取得することができる。(エンドポイントが1つで済む) データ型を定義することより、サーバーサイド側とのデータ型の不整合を防ぐことができる。 上記の特徴とは別に、GraphQLを実装する際にスキーマを定義する必要があるわけですが、このスキーマがそのままドキュメントになるという点はすごく良いですよね。 どこから手を付けようか色々と調べた結果、OREIlly本「初めてのGraphQL」の購入に至りました。 主観ではありますが、ページ量も程々に体系的に学べるようにまとまっており、ハンズオン形式ですすめるのが好みの僕には向いていた書籍でした。 初めてのGraphQL 今日では多くのWebサービスがRESTアーキテクチャスタイルで実装されています。RESTは2000年にフィールディングの論文で提唱された後に爆発的に普及し洗練されてきました。一方で、本書で紹介するGraphQLは2015年にFacebookによって公開されたRESTとは異なるアプローチのアーキテクチャです。GraphQ... www.oreilly.co.jp ですが、こちらの書籍は出版されてから少し日が経っており(初版第二刷が2020/4/15と記載)、文中のリンク先が現在は使用できないものや、記載されているコードがそのまま使用出来ないなど、ちらほら調べる必要がありました。 リンク先くらいであれば特段苦労しないのですが、言い回しの部分などで少々わかりにくい箇所は手が止まってしまうことがあり、「そういうことか!」と理解するのに時間がかかってしまいました。(翻訳されている文章なので、言い回しが独特になるのは実際仕方ないと思いますが) 以下では、書籍中で僕がつまづきそうになった(というか、つまづいた)箇所をいくつかご紹介します。同じような箇所で時間を浪費されている方がいれば参考にしていただければなと思います。 本の構成 本は全七章で構成されています。 1章から2章はGraphQlの歴史や背景、グラフ理論についてなどの紹介がメイン。 3章から4章はGraphQLの書き方、お作法、クライアントツールの紹介とスキーマ定義について解説。 5・6章ではGraphQLサーバー、GraphQLクライアントの実装。 7章では、GraphQLを本番環境で動かす想定として、セキュリティの部分やリアルタイムでの更新のためのWebSocketを実装するなど。 「体系的に学べる」系の本は概ね前半までは用語とか概念、お作法といった学ぶ上での事前知識を植え付けさせて、後半にかけて実際にやってみる。 といった流れで進んでいくので、「ある程度知ってるからとりあえず動かしたい!」って方は前半飛ばすのも全然ありかなと思います。(その際は、この本は五章からが対象になります。) カスタムスカラー型 書籍では、「5.2.5 カスタムスカラー型」の章になります。 GraphQLには、 Boolean ,  Int ,  Float ,  String ,  ID といった標準のスカラーが存在します。 それとは別に独自スカラーを定義する事もできます。(本書中ではDateTime型として作成します) カスタムスカラーは5章で登場するのですが、ここで3つのメソッドが紹介されます。 serialize →定義してある関数に従い、レスポンスに含める処理を行う。 parseValue →クライアント側から送る値を引数に追加する前に任意の処理を行う。 parseLiteral →クライアント側から送る値がリテラル(ハードコーディング)の際に実行する処理を行う。 それぞれクライアント側から引数を伴うリクエストを行った際に使用されるメソッドですが、書籍中では、いきなり解説に入るのでドキュメントを先に見ておくとスムーズに理解できそうです。 Custom scalars www.apollographql.com MongoDBのインストール 書籍では、「5.4.1 MongoDBのインストール」の章になります。 こちらについてはつまづいたわけではないんですが、「わざわざインストールしたくないなぁ。。」 ということ個人的な意向もあり、Dockerで解決させました。 ただし、書籍内での解説内容などにも影響があるので、推奨はしません。ある程度自分で解消できるのであればこの選択もあるよ。という程度に認識してください。 新規でdocker-composeを作成します。 docker-compose.yml version: '3' services: mongo: image: mongo:5.0.3 ports: - "27017:27017" volumes: - ./mongo:/docker-entrypoint-initdb.d environment: MONGO_INITDB_ROOT_USERNAME: user MONGO_INITDB_ROOT_PASSWORD: password MONGO_INITDB_DATABASE: example TZ: Asia/Tokyo 書籍内では.envでDB_HOSTの環境変数を以下のように表現してます。 DB_HOST=mongodb://localhost:27017/<Your-Database-Name> 上記の内容を置き換えます。 DB_HOST=mongodb://user:password@127.0.0.1:27017/example?authSource=admin 「authSource=admin」を指定している理由として、authSource が指定されていない場合、authSource のデフォルトは、接続文字列で指定された defaultauthdb になります。 defaultauthdb が指定されていない場合、authSource のデフォルトは admin になるためです。(以下に公式リンク貼っておきます) Connection String URI Format — MongoDB Manual www.mongodb.com githubAuthミューテーション 書籍では、「5.5.3 githubAuthミューテーション」の章になります。 こちらでは、GithubAPIでユーザー認証を経て、情報を取得する流れで進みます。 認可コード取得後にクライアントからgithubAuth ミューテーションを送信する手順ですが、このままではアクセストークンが返ってきません。 これはGithubのAPI認証が既存のコードだと非推奨の形になっているためです。 以下のリンクで確認できます。 Deprecating API authentication through query parameters Get started with one of our guides, or jump straight into the API documentation. developer.github.com 中身を見てもらえればわかると思いますが、トークンをクエリパラメータに含めず、ヘッダーで渡してねってことなので、記載のコードを修正します。(書籍では、lib.jsが対象のファイルです) lib.js const requestGithubUserAccount = token => fetch(`https://api.github.com/user?access_token=${token}`) .then(res => res.json()) こちらを以下のように書き換えます。 const requestGithubAccount = token => fetch(`https://api.github.com/user`, { method: 'POST', headers: { 'Authorization': `token ${token}`, } } ) 実は自分の環境では認証は通るようになったものの、アクセストークンがレスポンスに返って来なかったため、暫定対処として適当な箇所にconsole.logを置いてそこから取り出しました。 読み終わった感想 古い情報が多かったとはいえ、なんとか読み進めることが出来てちょっとした達成感はありました。もしかしたらそのうち新しいGraphQL本が出るかもしれませんね。 ハンズオン形式で学ぶのであれば、UdemyとかCouseraみたいなプラットホームもありますし、本よりもつまづきにくいと思いますが、説明や解説については活字のほうが頭に入ってくる印象です。(これは個人差だと思いますが(笑)) あと、この書籍ではApolloClient、ApolloServerを使って手順をすすめるので、公式のドキュメントを見ながら読み進める方が良いと思います。 みなさんもどこかでGraphQLを使う機会があれば、こちらの書籍を手にとってみては如何でしょうか? ではまた! The post OREIlly本「初めてのGraphQL」をつまづかないために。 first appeared on miracleave Tech Blog .
アバター
はじめに お久しぶりです!miracleave新垣です!今年のゴールデンウィークも終わってしましたね!皆さんはどのように過ごしましたでしょうか?私は実家にかえってのんびり田舎ライフを満喫しました!  今回は私の次の案件がruby on railsを使ったプロジェクトということと、前からちゃんと勉強しようしようと放置していた  Github Actionsの勉強がてらに rails (rspec) を使ってCIの構築をしてみようと思います!  先に環境構築を行ってGithub Actionsの解説を行います! 環境構築 まず、rails環境の準備を行いたいと思います。今回はdockerを使って環境構築を行いました。今回はGithub Actionsの解説をメインとします。dockerとrailsに関する細かい設定等の話は省略させて頂きます。作成したアプリケーションはgithub上にありますので確認してください。ここではディレクトリ構成、Dockerfile等のコードのみ紹介します。以下リンクを参考にいたしました。 【Dockerで環境構築】Rails 6 & MySQL 8 - Qiita 1. はじめに Railsで新しくアプリケーションをつくってみようと思いたち環境構築をしたので、備忘録用の記事です。 エンジニア歴 & Rails歴が1年弱ですので、間違いやよりベターな書き方があればご指摘ください。 ... qiita.com GitHub - 20yuteo/rails_cicd Contribute to 20yuteo/rails_cicd development by creating an account on GitHub. github.com ディレクトリ構成 rails_app/ ├ app/ ├ bin/ ├ config/ ├ db/ ├ lib/ ├ log/ ├ public/ ├ spec/ ├ storage/ ├ tmp/ ├ vendor/ ├ Dockerfile ├ .env ├ Gemfile ├ Gemfile.lock ├.github/workflows/testing.yml └ docker-compose.yml Dockerfile FROM ruby:2.7.2 ENV LANG C.UTF-8 ENV APP_ROOT /app # install required libraries RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get update -qq && \ apt-get install -y --no-install-recommends \ build-essential RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - RUN apt-get install -y --no-install-recommends \ nodejs \ yarn && \ apt-get clean && \ rm --recursive --force /var/lib/apt/lists/* # create working directory RUN mkdir $APP_ROOT WORKDIR $APP_ROOT # bundle install COPY Gemfile $APP_ROOT/Gemfile COPY Gemfile.lock $APP_ROOT/Gemfile.lock RUN bundle install --jobs 4 --retry 3 # create app in container COPY . $APP_ROOT # script to be executed every time the container starts COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process CMD ["rails", "server", "-b", "0.0.0.0"] docker-compose.yml version: '3.7' services: db: image: mysql:8.0.20 volumes: - mysql:/var/lib/mysql:delegated ports: - '3306:3306' command: --default-authentication-plugin=mysql_native_password env_file: .env web: build: context: . dockerfile: Dockerfile command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" tty: true stdin_open: true env_file: .env depends_on: - db ports: - '3000:3000' volumes: - .:/app:cached - bundle:/usr/local/bundle:delegated - node_modules:/app/node_modules - tmp-data:/app/tmp/sockets volumes: mysql: bundle: node_modules: tmp-data: .env MYSQL_ROOT_PASSWORD=password TZ=Japan Gemfile source 'https://rubygems.org' gem 'rails', '6.0.3' entrypoint.sh #!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@" 2. Github Actions の設定   以下のyamlファイルを確認してみましょう。Github Actionsの設定が記載されています。   .github\workflows\testing.yml name: testing on: push: pull_request: jobs: rspec: runs-on: ubuntu-latest timeout-minutes: 10 services: mysql: image: mysql:8.0.20 ports: - 3306:3306 env: MYSQL_ALLOW_EMPTY_PASSWORD: yes options: --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 10 steps: - name: Checkout code uses: actions/checkout@v2 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: bundler-cache: true - name: Cache node modules uses: actions/cache@v2 with: path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-node- - name: Bundler and gem install run: | gem install bundler bundle install --jobs 4 --retry 3 --path vendor/bundle - name: Yarn install run: yarn install --check-files - name: Database create and migrate run: | cp config/database.yml.ci config/database.yml bundle exec rails db:create RAILS_ENV=test bundle exec rails db:migrate RAILS_ENV=test - name: Run rspec run: bundle exec rspec Github Actions のリファレンスを参照して testing.yml の内容を確認しましょう。 GitHub Actionsのワークフロー構文 - GitHub Docs ワークフローは、1つ以上のジョブからなる設定可能な自動化プロセスです。 ワークフローの設定を定義するには、YAMLファイルを作成しなければなりません。 ghdocs-prod.azurewebsites.net 2-1. name name: testing ワークフローの名前。 GitHubでは、リポジトリのアクションページにワークフローの名前が表示されます。 nameを省略すると、GitHubはリポジトリのルートに対するワークフローファイルの相対パスをその値に設定します。 そのままですね。githubの Actions タブのAll workflowsに設定した名前で表示されます。   2-2. on on: push: pull_request: To automatically trigger a workflow, use on to define which events can cause the workflow to run.   on に設定されたトリガーのタイミングで workflow が動作します。記載方法が多数あります。   今回は push と pull_request のタイミングで動作するように設定しています。詳細は公式ドキュメントを参照してください。 2-3. jobs jobs: rspec: A workflow run is made up of one or more jobs, which run in parallel by default.   workflow の実行を構成する1つ以上のステップのことです。今回は rspec という job のみですが複数設定可能です。 2-4. runs-on runs-on: ubuntu-latest timeout-minutes: 10 runs-on はgithubが提供しているホストランナーになります。今回は ubuntu を選択しています。  time-out-minutes は githubで自動的にキャンセルされるまでジョブを実行する最長時間になります。デフォルトで360分となっています。   2-5. services services: mysql: image: mysql:8.0.20 ports: - 3306:3306 env: MYSQL_ALLOW_EMPTY_PASSWORD: yes options: --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 10 ワークフロー中のジョブのためのサービスコンテナをホストするために使われます。 サービスコンテナは、データベースやRedisのようなキャッシュサービスの作成に役立ちます。 ランナーは自動的にDockerネットワークを作成し、サービスコンテナのライフサイクルを管理します。   今回はrailsアプリケーションに mysql を使用しているので上記のように設定しました。 2-6. steps steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: bundler-cache: true - name: Cache node modules uses: actions/cache@v2 with: path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-node- - name: Bundler and gem install run: | gem install bundler bundle install --jobs 4 --retry 3 --path vendor/bundle - name: Yarn install run: yarn install --check-files - name: Database create and migrate run: | cp config/database.yml.ci config/database.yml bundle exec rails db:create RAILS_ENV=test bundle exec rails db:migrate RAILS_ENV=test - name: Run rspec run: bundle exec rspec 1つのジョブには、steps (ステップ) と呼ばれる一連のタスクがあります。 ステップでは、コマンドを実行する、設定タスクを実行する、あるいはリポジトリやパブリックリポジトリ、Dockerレジストリで公開されたアクションを実行することができます。 すべてのステップでアクションを実行するとは限りませんが、すべてのアクションはステップとして実行されます。   job に紐づくタスクのことを step と呼びます。ひとつづつ確認してみましょう。 2-7. actions/checkout@v3   - name: Checkout code uses: actions/checkout@v3 name は step の名前になります。uses はジョブでステップの一部として実行されるアクションを選択します。  対象のソースを actions/checkout@v3 でチェックアウトします。 2-8. ruby/setup-ruby@v1 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: bundler-cache: true ruby/setup-ruby@v1 はビルド済みの ruby をダウンロードしてきます。細かい設定等は以下を参照してください。   GitHub - ruby/setup-ruby: An action to download a prebuilt Ruby and add it to the PATH in 5 seconds An action to download a prebuilt Ruby and add it to the PATH in 5 seconds - GitHub - ruby/setup-ruby: An action to download a prebuilt Ruby and add it to the PA... github.com 2-9. actions/cache@v2 - name: Cache node modules uses: actions/cache@v2 with: path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-node- actions/cache@v2 依存関係をキャッシングして workflow のスピードを上げるものです。  今回は node_modulues をキャッシュするために使用しました。 依存関係をキャッシュしてワークフローのスピードを上げる - GitHub Docs ワークフローを高速化して効率を上げるために、依存関係や広く再利用されるファイルに対するキャッシュを作成して利用できます。 ghdocs-prod.azurewebsites.net 2-10. Bundler and gem install - name: Bundler and gem install run: | gem install bundler bundle install --jobs 4 --retry 3 --path vendor/bundle 上記の処理は記載している通りでbundlerをインストールしてrailsアプリケーションに必要な gem を bundle install で取得しています。 2-11. Yarn install - name: Yarn install run: yarn install --check-files こちらも記述通りで yarn install によって必要なパッケージをインストールしています。 2-12. Database create and migrate - name: Database create and migrate run: | cp config/database.yml.ci config/database.yml bundle exec rails db:create RAILS_ENV=test bundle exec rails db:migrate RAILS_ENV=test rails に必要な gem が install 出来たので database を作成し migration を実行します。その際に必要なファイルは以下に用意しています。   config/database.yml.ci - name: Run rspec run: bundle exec rspec 実行環境が整いました。いよいよテスト実行です。実際に実行すると以下のように actions で実行状況等が確認できるはずです。 3. まとめ 公式リファレンスなどを参照するとどのようなことが設定されているのが分かりやすくまとめられており導入しやすさを感じました!  実際の現場で採用する際にはもう少し細かい設定も必要になるかと思いますので今回の内容で取り上げたリンクなどを参考にしてみてください! The post Github Actionsで CIの構築をしてみた! first appeared on miracleave Tech Blog .
アバター
Dagger という CI/CD のツールが話題になっているのをみかけました。 なんでも Docker の創始者さんが開発しているとか。 どんなものかと Getting Started をなぞってみました。今回はその記録です。 Getting Started | Dagger docs.dagger.io まずは CI/CD in your local dev のページから実行します。 業務では windows の利用が多いのですが、私物では MacBook Pro を使っているので macOS の手順に従い実行します。 まずは brew でインストール # brew でのインストールが久しぶりだったので update から始めています $ brew update -v # dagger をインストールします $ brew install dagger/tap/dagger # バージョンの確認 $ dagger version [22-04-23_16:48] dagger 0.2.7 (18c19174) darwin/amd64 無事に完了。 次にサンプルのソースを取得してビルドします。 # プロジェクトを clone してディレクトリに移動するお決まりの操作 $ git clone https://github.com/dagger/dagger $ cd dagger # オプションなしのコピペでは失敗したのでとりあえず新しいブランチに切り替えておく $ git checkout -b v0.2.7 # サンプルのディレクトリに移動して内容の確認 $ cd pkg/universe.dagger.io/examples/todoapp $ ls README.md package.json public src todoapp.cue yarn.lock # ビルドの実行 $ dagger do build [✔] actions.build.run.script 0.1s [✔] actions.deps 0.1s [✔] client.filesystem."./".read 0.2s [✔] actions.test.script 0.1s [✔] actions.test 1.3s [✔] actions.build.run 13.3s [✔] actions.build.contents 0.0s [✔] client.filesystem."./_build".write 0.1s $ ls README.md _build package.json public src todoapp.cue yarn.lock ビルドが完了したようです。 _build というディレクトリが作成されていますね。 そのしたに index.html が作られているようなので確認しましょう。 # ビルド結果の確認 $ open _build/index.html タスク管理アプリの画面がでているかと思います。これがサンプルアプリの内容のようですね。 macOS 上で実行しているにもかかわらずいろいろな依存関係のインストールが不要であるのが Daggar の素敵ポイントの一つのようです。 少し内容を変更して再ビルドします # ファイル内容の変更 $ vi src/components/Form.js <h2 className="label-wrapper"> <label htmlFor="new-todo-input" className="label__lg"> - What needs to be done? + What must be done today? </label> </h2> # 再ビルド $ dagger do build [✔] actions.build.run.script 0.0s [✔] actions.deps 0.0s [✔] client.filesystem."./".read 0.0s [✔] actions.test.script 0.0s [✔] actions.test 1.4s [✔] actions.build.run 6.8s [✔] actions.build.contents 0.0s [✔] client.filesystem."./_build".write 0.1s # 画面の確認 $ open _build/index.html トップのメッセージが変更したものに変わっていますね。 Dagger を使うとビルドとテストが細やかに行えるってことでしょうかね。 ここまでがローカル環境での利用手順になります。 次に他の CI/CD のサービスから利用する方法です。 私は GitLab が好きなので GitLab で試してみます。 taskapp のディレクトリを切り出して GitLab に push してみます。 CI/CD の設定ファイルである .gitlab-ci.yml を追加します。 以下のようなディレクトリ構成です。 . ├── .git ├── .gitignore ├── .gitlab-ci.yml < 追加 ├── README.md ├── package.json ├── public ├── src ├── todoapp.cue └── yarn.lock .docker: image: docker:${DOCKER_VERSION}-git services: - docker:${DOCKER_VERSION}-dind variables: # See https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#docker-in-docker-with-tls-enabled-in-the-docker-executor DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_VERIFY: '1' DOCKER_TLS_CERTDIR: '/certs' DOCKER_CERT_PATH: '/certs/client' # Faster than the default, apparently DOCKER_DRIVER: overlay2 DOCKER_VERSION: '20.10' .dagger: extends: [.docker] variables: DAGGER_VERSION: 0.2.7 DAGGER_LOG_FORMAT: plain DAGGER_CACHE_PATH: .dagger-cache ARGS: '' cache: key: dagger-${CI_JOB_NAME} paths: - ${DAGGER_CACHE_PATH} before_script: - apk add --no-cache curl - | # install dagger cd /usr/local curl -L https://dl.dagger.io/dagger/install.sh | sh cd - dagger version script: - dagger project update - | dagger \ do \ --cache-from type=local,src=${DAGGER_CACHE_PATH} \ --cache-to type=local,mode=max,dest=${DAGGER_CACHE_PATH} \ ${ARGS} build: extends: [.dagger] variables: ARGS: build パイプラインの実行でエラーが出てしまいました。 $ dagger project update 3:53PM FTL system | dagger project not found. Run `dagger project init` Cleaning up project directory and file based variables 00:01 ERROR: Job failed: exit code 1 ビルドが通るように修正しましょう。 プロジェクトを切り出してしまったせいでモジュールの定義が足りなくなってしまったようです。 .gitlab-ci.yml にコマンドを追加しました。 dagger version script: + - dagger project init - dagger project update dagger project init で cue.mod が作られてこれを基に dagger project update が実行されるようです。 再実行すると再びエラーが発生。 $ dagger \ # collapsed multi-line command time="2022-04-23T16:38:26Z" level=warning msg="commandConn.CloseRead: commandconn: failed to wait: signal: terminated" time="2022-04-23T16:38:26Z" level=warning msg="local cache import at .dagger-cache not found due to err: could not read .dagger-cache/index.json: open .dagger-cache/index.json: no such file or directory" time="2022-04-23T16:38:26Z" level=warning msg="local cache import at .dagger-cache not found due to err: could not read .dagger-cache/index.json: open .dagger-cache/index.json: no such file or directory" server.go:647: http2: server connection error from localhost: connection error: PROTOCOL_ERROR 4:38PM FTL system | failed to execute plan: specifying multiple cache exports is not supported currently Cleaning up project directory and file based variables 00:01 ERROR: Job failed: exit code 1 dagger の実行でエラーが発生しているようです。 やや乱暴ですがオプションを削除してみました。 - | dagger \ do \ - --cache-from type=local,src=${DAGGER_CACHE_PATH} \ - --cache-to type=local,mode=max,dest=${DAGGER_CACHE_PATH} \ ${ARGS} いったんこれでローカル同様 dagger do build が通るようになりました。 デプロイのプロセスに一つレイヤーが追加されるような感じですね。 まだまだ全然実行の詳細が理解できてませんが、実行の内部詳細を確認していきたいと思います。 今回は GitLab で試してみましたが、これからいろいろ Getting Started のテンプレートも増えるみたいですね。楽しみです。 The post Dagger の Get started やってみた first appeared on miracleave Tech Blog .
アバター
はじめに 上野です! 最近、GraphQL(Golang)とNext.jsを使用して簡単なWebアプリを作ったりしています。今回はこのGraphQLとNext.jsの簡単 Webアプリを作る中で構築した開発環境をご紹介していきたいと思います。 今回はVSCodeのRemote Containersを使用して構築していきます。 Golang側に関しては、ホットリロード、デバックができるようにし、Next.js側に関してはPrettierなどを入れてより開発がしやすいようにしていきます。 では、早速構築していきます! フォルダ構成 フォルダ構成は以下のように作成してください。 go-next |_server | |_.devcontainer | | |_devcontainer.json | |_.vscode | | |_launch.json | |_Dockerfile | |_.air.toml | |_main.go | |_client | |_.devcontainer | | |_devcontainer.json | |_Dockerfile | |_docker-compose.yml docker-compose.ymlには以下のコードを書いてください。 version: '3.8' services: server: build: context: ./server/ dockerfile: Dockerfile target: dev ports: - 8888:8080 - 2345:2345 volumes: - ./server/:/app client: build: context: ./client/ dockerfile: Dockerfile target: dev volumes: - ./client:/app ports: - 3434:3000 go-next/server/main.goには以下のコードを書いてください。 package main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/greeting", func(c *gin.Context) { greeting := "hello world" c.JSON(200, gin.H{ "message": greeting, }) }) r.Run() } VSCodeの設定 まずはVSCodeでgo-nextフォルダを開き、Extensionsで「Remote – Containers」をインストールします。 Golang側のVSCodeの設定 go-next/server/.devcontainer/devcontainer.jsonには以下のコードを書いてください。 { "name": "Go devcontainer", "dockerComposeFile": [ "../../docker-compose.yml" ], "service": "server", "workspaceFolder": "/app", "settings": { "go.toolsManagement.checkForUpdates": "off", "go.gopath": "/go", "go.gocodeAutoBuild": true, "go.formatTool": "gofmt", "go.useLanguageServer": true, "editor.tabSize": 2, "editor.formatOnPaste": true, "editor.formatOnSave": true, "editor.formatOnType": true, "editor.renderWhitespace": "all", "editor.bracketPairColorization.enabled": true, "editor.guides.bracketPairs":"active", "files.trimTrailingWhitespace": true, "files.trimFinalNewlines": true, "emeraldwalk.runonsave": { // デバック時、airによって.tmp/mainのプロセスが起動されるので、ファイル保存時に強制的に.tmp/mainのプロセスを削除する "commands": [ { "match": ".*", "isAsync": false, "cmd": "ps aux | grep './tmp/main' | grep -v grep | awk '{ print $1 }' | xargs kill" }, ] } }, "extensions": [ "golang.go", "editorconfig.editorconfig", "emeraldwalk.runonsave", // コードのスペルチェッカー "streetsidesoftware.code-spell-checker", ], "onCreateCommand": "", "updateContentCommand": "", "postCreateCommand": "", "shutdownAction": "none" } go-next/server/Dockerfileには以下のコードを書いてください。 FROM golang:1.17-alpine as dev WORKDIR /app RUN apk update && apk add git RUN go install golang.org/x/tools/cmd/goimports@latest \ && go install golang.org/x/tools/gopls@latest \ && go install golang.org/x/tools/cmd/godoc@latest \ && go install golang.org/x/lint/golint@latest \ && go install github.com/rogpeppe/godef@latest \ && go install github.com/nsf/gocode@latest \ # hot relord && go install github.com/cosmtrek/air@latest \ # debug && go install github.com/go-delve/delve/cmd/dlv@latest CMD ["air", "-c", ".air.toml"] go-next/server/.vscode/launch.jsonには以下のコードを書いてください。 { "version": "0.2.0", "configurations": [ { "name": "Go", "type": "go", "request": "attach", "mode": "remote", "cwd": "${workspaceFolder}", "port": 2345, "host": "0.0.0.0", "showLog": true, } ] } go-next/server/.air.tomlには以下のコードを書いてください。 現在の.ari.tomlはデバック用になっています。デバック用ではなく、普通のホットリロードの状態にしたい場合はDebugとコメントがある下の部分のfull_binをコメントアウトし、Customize binaryとコメントがある下の部分のコメントアウトを解除すると普通のホットリロードの状態に戻ります。 root = "." tmp_dir = "tmp" [build] bin = "./tmp/main -e=develop" cmd = "go build -o ./tmp/main ." # It's not necessary to trigger build each time file changes if it's too frequent. delay = 1000 # ms # Ignore these filename extensions or directories. exclude_dir = ["assets", "tmp", "vendor"] # Exclude files. exclude_file = [] exclude_regex = [] exclude_unchanged = false follow_symlink = false # Customize binary. # full_bin = "APP_ENV=dev APP_USER=air ./tmp/main" # Debug full_bin = "APP_ENV=dev APP_USER=air /go/bin/dlv exec ./tmp/main --headless=true --listen=:2345 --api-version=2 --accept-multiclient" # Watch these directories if you specified. include_dir = [] # Watch these filename extensions. include_ext = ["go", "tpl", "tmpl", "html"] # Delay after sending Interrupt signal kill_delay = 500 # ms # This log file places in your tmp_dir. log = "air.log" # Send Interrupt signal before killing process (windows does not support this feature) send_interrupt = false # Stop running old binary when build errors occur. stop_on_error = true [color] main = "magenta" watcher = "cyan" build = "yellow" runner = "green" [log] time = false [misc] # Delete tmp directory on exit clean_on_exit = true Next.js側のVSCodeの設定 client/.devcontainer/devcontainer.jsonには以下のコードを書いてください。 { "name": "Next devcontainer", "dockerComposeFile": [ "../../docker-compose.yml" ], "service": "client", "workspaceFolder": "/app", "settings": { "editor.tabSize": 2, "editor.formatOnPaste": true, "editor.formatOnSave": true, "editor.formatOnType": true, "editor.renderWhitespace": "all", "editor.bracketPairColorization.enabled": true, "editor.guides.bracketPairs":"active", "files.trimTrailingWhitespace": true, "files.trimFinalNewlines": true, }, "extensions": [ "xabikos.javascriptsnippets", "formulahendry.auto-rename-tag", "formulahendry.auto-close-tag", "coenraads.bracket-pair-colorizer-2", "shardulm94.trailing-spaces", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "visualstudioexptteam.vscodeintellicode", "foxundermoon.next-js", // コードのスペルチェッカー "streetsidesoftware.code-spell-checker", ], "shutdownAction": "none" } Next.jsのアプリを作成 docker-compose.ymlファイルが存在するディレクトリ上で以下コマンドを実行しNext.jsのアプリを作成していきます。 docker-compose run --rm client yarn create next-app --typescript 途中で「? What is your project named?」と聞かれるので「app」と入力します。 go-next/clientフォルダ直下に「yarn create next-app」で作成したappフォルダが作成されるので、このappフォルダの中身を./clientフォルダに移動していきます。 mv ./client/app/* ./client/app/.* ./client rm -r ./client/app 以上でNext.jsの用意が完了しました。 Golang、Next.jsのコンテナにアクセスしてみる docker-compose.ymlファイルが存在するディレクトリ上で以下コマンドを実行しコンテナを立ち上げます。 docker-compose up -d では、Golangのコンテナにアクセスしていきます。 GolangのRemote Containerに接続するにはVSCodeの左下のボタンをクリックし「Open Folder in Container」をクリックしgo-next/serverフォルダを選択します。 serverフォルダを選択すると自動でserverのRemote Containerに接続できます。 コンテナ内に入ってgoファイルを作成し、実際にコーディングをしてみてください。開発がしやすくなっているはずです。 Next.js用のコンテナへのアクセスも先程と同様(Golang側)でVSCodeの左下のボタンをクリックし「Open Folder in Container」をクリックしgo-next/clientフォルダを選択することでNext.jsのコンテナにアクセスすることができます。 Next.js側のコンテナも同様にファイル保存時に自動でフォーマットされたりなど開発がしやすくなっているのが分かります。 Golang側のDebugに関しては、Golang側のRemote Containerに接続しVSCodeのサイドメニューから「Run on Debug」をクリックし「Go」と記載してある隣の再生ボタンをクリックするとデバックが開始します。 デバックを開始する際は、.air.tomlファイル内のDebugとコメントアウトがされている下のfull_binという箇所のコメントアウトを解除し、Customize binaryとコメントアウトされている下のfull_binをコメントアウトするようにしてください。 以上でNext.jsとGolangの開発環境の構築は完了です。 まとめ 今回はGolang✖︎Next.jsの開発環境をご紹介しました。今回紹介したRemote Containersを使用しての開発環境ですが、個人ごとにVSCodeの設定をせずに済み、またローカル環境も汚さずに済むのでかなりお勧めです。他にもお勧めの設定方法などがあれば紹介していきます! 今回は以上となります。ありがとうございました! The post VSCodeでGolang✖︎Next.jsの開発環境を構築してみた first appeared on miracleave Tech Blog .
アバター
はじめに こんにちは!先日mira meet(弊社のミートアップ)で発表させていただいたRSpecのShoulda Matchersがめちゃくちゃ便利なのでこちらでもご紹介させていただきます。 Shoulda Matchersとは Shoulda MatchersとはRSpec内で使用できるライブラリで、複雑なテストをワンライナーで書くことができるマッチャのことです。 Shoulda MatchersのGitHub 通常のテストの場合 例えば、下記のようなUserモデルがあるとします。 バリデーションテストをする場合、下記のような観点が必要だとします。 ・ nameがnilの場合、無効であること ・ nameが空文字の場合、無効であること ・ nameが既に保存されている場合、無効であること ・ nameが10文字以内の場合、有効であること ・ nameが11文字以上の場合、無効であること ・ emailがnilの場合、無効であること ・ emailが空文字の場合、無効であること ・ emailが既に保存されている場合、無効であること ・ emailがemailの形式ではない場合、無効な状態であること ・ emailは全角文字を使用する場合、無効な状態であること ・ passwordがnilの場合、無効であること ・ passwordが空文字の場合、無効であること ・ passwordが5文字以内の場合、無効であること ・ passwordが6文字以上の場合、有効であること ・ passwordが128文字以内の場合、有効であること ・ passwordが129文字以上の場合、無効であること ・ password_confirmationがnilの場合、無効であること ・ password_confirmationが空文字の場合、無効であること ・ passwordとpassword_confirmationが不一致の場合、無効であること この時点で膨大ですが、アプリケーションが大きくなればなるほど必要な観点は多くなります。これをテストに起こすとなると非常に大変です。 では、通常通りRSpecを使用してテストを書いてみましょう。nameを例に書いてみると下記のようになります。 require 'rails_helper' RSpec.describe User, type: :model do describe 'name' do before do @user = User.new( name: "Tanaka", email: 'tester@example.com', password: 'p@ssword!!', password_confirmation: 'p@ssword!!', ) end it 'nilの場合、無効であること' do @user.name = nil @user.valid? expect(@user.errors[:name]).to include("can't be blank") end it '空文字の場合、無効であること' do @user.name = "" @user.valid? expect(@user.errors[:name]).to include("can't be blank") end it 'すでに使用されているnameの場合、保存できないこと' do User.create( name: 'Takashi', email: 'tester_1@example.com', password: 'p@ssword!!', password_confirmation: 'p@ssword!!', ) @user.name = 'Takashi' @user.valid? expect(@user.errors[:name]).to include("has already been taken") end it '10文字以内の場合、有効であること' do @user.name = 'TakashiKai' expect(@user).to be_valid end it '11文字以上の場合、無効であること' do @user.name = 'TakashiKaii' @user.valid? expect(@user.errors[:name]).to include("is too long (maximum is 10 characters)") end end end 少し冗長ですね。特にnameの値がない場合のテストでは、空文字、nullどちらもテストが必要ですし、文字数も境界線になる10文字と11文字でテストしないといけないのが辛いところです。 Shoulda Matchersを使用したテストの場合 ではShoulda Matchersを使用してテストを実装してみましょう。 require 'rails_helper' RSpec.describe User, type: :model do describe 'name' do it { is_expected.to validate_presence_of :name } it { is_expected.to validate_uniqueness_of :name } it { is_expected.to validate_length_of(:name).is_at_most(10) } end end !? なんと実際のテスト実行部分が3行になってしまいました! ・ nameがnilの場合、無効であること ・ nameが空文字の場合、無効であること こちらの観点は it { is_expected.to validate_presence_of :name } という一文でテストがされています。もしわかりづらければ、下記のように書き換えて実行してみてください。 require 'rails_helper' RSpec.describe User, type: :model do describe 'name' do user = User.new( name: "", # またはname: nil email: 'tester@example.com', password: 'p@ssword!!', password_confirmation: 'p@ssword!!', ) it { expect(user).to validate_presence_of :name } end end userインスタンスのnameの値を空文字にしてもnilにしてもテストが通ることが確認できると思います。 ・ nameが既に保存されている場合、無効であること こちらの観点は it { is_expected.to validate_uniqueness_of :name } の一文でテストされています。 ・ nameが10文字以内の場合、有効であること ・ nameが11文字以上の場合、無効であること こちらの観点は it { is_expected.to validate_length_of(:name).is_at_most(10) } の一文でテストされています。こちらの10という数字を9や11にしてテストしてみてください。例えば9にした時は下記のようなエラーが出ます。 Failures: 1) User name is expected to validate that the length of :name is at most 9 Failure/Error: it { is_expected.to validate_length_of(:name).is_at_most(9) } Expected User to validate that the length of :name is at most 9, but this could not be proved. After setting :name to ‹"xxxxxxxxxx"›, the matcher expected the User to be invalid, but it was valid instead. # ./spec/models/user_spec.rb:5:in `block (3 levels) in <top (required)>' Finished in 0.47804 seconds (files took 3.31 seconds to load) 1 example, 1 failure 9文字が最大だから ‹"xxxxxxxxxx"› のように10文字で保存しようとすると無効になることを期待したが、有効になっているとのことです。つまり、9文字なら有効、だけでなく10文字なら無効というテストも行っていることがわかります。 このようにShoulda Matchersを使用すると非常に簡単に簡潔にテストを記述することができるようになります。また、今回はモデルを例にご紹介しましたが、ActiveRecordやController、Routingでも使用できるマッチャが揃っています。興味がある方はぜひGitHubを覗いてみてください。 まとめ 今回はShoulda Matchersをご紹介しました。テストはアプリケーションが大きくなればなるほど管理が難しく、冗長になりやすい傾向があります。しかし、このマッチャを使用すれば、簡素に書くことができますし、エラー内容もわかりやすいなどのメリットもあるため、ぜひ利用をお勧めします。 では!よいテストコードライフを!! The post RSpecをShoulda Matchersを使ってめちゃ簡単に書く first appeared on miracleave Tech Blog .
アバター
こんにちは!話題のELDEN RINGを買ったはいいけどあまり遊べていない竹村です。 現場業務にて、JavaScriptのフレームワークであるKonva.jsとjsPDFを使って、画面に描画されているstage要素をPDF化しようとしたところ、stage要素のスケールも含めてPDF化されてしまい、stage全体がPDF化されない現象が起こったため、その解決方法を書いていきたいと思います。 尚、環境はVue2系となります。 ※stageはマウスホイール操作で拡大縮小できるようにしていた為、画面上に描画されているスケール通りにPDFが生成されてしまっていました。 Konva.jsとは? Konva.jsはJavaScriptのフレームワークで、divタグ内のcanvas要素を作成し、図形の生成や編集、画像の配置等ができます。stageという最下層のレイヤーの上に、複数のlayerを追加して画面描画できます。 座標やスケールを操作する事で、stage上の要素を拡大縮小したり、html2Canvasを使用する事で画像化できたりと、とても汎用性の高いフレームワークとなっています。 jsPDFとは? JavaScriptのライブラリです。生成されたjsPDFインスタンスに対し、テキストや図形、画像等を追加し、簡単にPDFを作成してくれます。 また、jsPDFインスタンスはarraybufferやblobといった形で出力できるので、base64形式にエンコードしてDBに保存もできたりします。 英語ですが公式ドキュメントも充実していますので、使い勝手の良いライブラリとなっています。 結論から言うと stage要素全体がPDF化されない件について結論から述べますと、stageのscaleとpositionを初期化する事で解決しました。 まず、pdf化させる関数内でstageを取得します。(関数の引数はselfとしています) var stage = self.$refs["stage"]; var getStage = stage.getStage(); $refs[“stage”]で取得したstage要素から、getStage()することでstage要素を取得できます。 (最初、$refsで取得した段階でstageが取れたと思っていたのですが、stageをコンソールにログ出力したところ、更にgetStage()する必要があるとわかりました。ややこしい。。。) stage要素を取得できたところで、scaleとpositionを初期化してあげます。 getStage.setScale({ x: 1, y: 1 }); getStage.setPosition({ x: 0, y: 0 }); scaleは拡大縮小率なので、等倍である1に設定します。 positionは、画面上でstage要素をドラッグで動かしている場合があるので、座標位置を0に戻してあげます。 これで、stage要素を等倍かつ正しくPDF化させることができました。 PDF化関数の全体的なソースは、こんな感じです。 function downloadPdf(self) { //stageを取得 var stage = self.$refs["stage"]; var getStage = stage.getStage(); //pdfサイズとなる縦横の幅を取得 var width = getStage.attrs.width; var height = getStage.attrs.height; //pdfインスタンスを生成 var pdf = new jsPDF({ orientation: "l", unit: "px" format: [width, height], compress: true, }); //現状のscaleとpositionを取得 var scale = getStage.getScale(); var position = getStage.getPosition(); //scaleとpositionを初期化 getStage.setScale({ x: 1, y: 1 }); getStage.setPosition({ x: 0, y: 0 }); //pdf化処理 var output = getStage.toImage({ callback(image) { //pdfインスタンスにimageを追加 pdf.addImage(image, 10, 10, width, height); //scaleとpositionを元に戻す getStage.setScale(scale); getStage.setPosition(position); //pdfダウンロード pdf.save("hoge.pdf"); }, mimeType: "image/png", x: 10, y: 10, width: width, height: height, }); } 解説 7、8行目で縦幅と横幅を取得していますが、PDF出力させたい幅を固定値で入れても問題ありません。例として、ここではstageそのものの縦横としています。 11行目で、jsPDFインスタンスを作成しています。作成する際、引数をいくつか設定しています。 orientation:pdfを出力する際の向き(縦か横か)を指定できます。ここでは、設定値に”l”(landscapeのl)を指定している為、出力される際は横向きになります。 unit:出力する際の単位を指定します。デフォルトはpxなので、わざわざ書かなくても大丈夫です。 format:縦横幅のサイズです。a4やb5などといった指定の仕方もできますし、今回のように配列で直接指定することもできます。 compress:pdfを出力する際、圧縮するかどうかをtrueかfalseで指定できます。自分が試したときは、指定しないとファイルサイズがとんでもないことになったので、trueにすることをお勧めします。。。 19、20行目では、現状のscaleとpositionを取得しています。 一旦保持しておき、これらを初期化した後に33、34行目で元の値に戻しています。 実際のPDF化している部分では、Konva.jsのStageが持つtoImage()メソッドを使用して、stage要素を一旦画像にしたうえで(callback関数の引数imageが画像です)、pdfインスタンスのaddImage()メソッドに渡しています。 ポイントとしては、toImage()メソッドの第一引数がcallback関数の為、その中でpdfインスタンスにimageを渡し、save処理していることです。 終わりに いかがでしたでしょうか。スケールとポジションを一旦初期化している為、画面上は一瞬stage要素の大きさが変化してしまいますが、理想の形でPDFが作成されるのであれば許容範囲といったところでしょうか。 それでは、皆さんも良きPDFライフを! The post jsPDFを使ってKonvaのstage要素をPDF化する際に困った話 first appeared on miracleave Tech Blog .
アバター
こんにちわ!相も変わらずGolangが大好きな私、佐々木です! 今日はGolangのフレームワーク、 Ginを使っている中で独自のバリデータを作るのがとても簡単だったのでご紹介します。 Gin GinはGolangのWebフレームワークで、同じくGolangのWebフレームワーク「Martini」にインスパイアされた非常に高パフォーマンスかつ、人気のフレームワークです。 また、GithubStarも数あるGolangのWebフレームワークの中で最多となっています。 少し前のリストですが、順位の変動はなさそうです。 GitHub - mingrammer/go-web-framework-stars: Web frameworks for Go, most starred on GitHub :star: Web frameworks for Go, most starred on GitHub - GitHub - mingrammer/go-web-framework-stars: Web frameworks for Go, most starred on GitHub github.com デフォルトバリデーター Ginでは、デフォルトのバリデーターとして https://github.com/go-playground/validator を使用しています。一例としてですが、バリデーターを適用させる際に、入力される最小値を1とするなら以下のように書きますね。(binding:”required,min=1″) type transferRequest struct { FromAccountID int64 `json:"from_account_id" binding:"required,min=1"` ...... } 独自項目などでバリデーターを書くのであれば以下のような書き方も可能です。 こちらは貨幣の略称をバリデーターにかけたものです。(binding:”required,oneof=USD EUR JPY”) type createAccountRequest struct { ...... Currency string `json:"currency" binding:"required,oneof=USD EUR JPY"` } これでもやりたいことは出来ますが、対象項目が10、20と多くなれば、このような書き方はあんまりしたくないですね。。。 カスタムバリデーター ということで、カスタムバリデーターを調べたらこちらに作成方法がありました。 examples/server.go at master · gin-gonic/examples A repository to host examples and tutorials for Gin. - examples/server.go at master · gin-gonic/examples github.com この内容を参考に作成してみます。 API部分をちょっと雑でしたが書いてみました。こちらの7行目に記載している、 「v.RegisterValidation(“currency”, validCurrency)」の第一引数にはバリデーションとして使用するタグ名、第二引数は関数を記載します。 (”github.com/go-playground/validator/v10″のimport文では末尾の “v10” を忘れずに記載します。) import ( ...... "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" ) type Server struct { func NewServer(store db.Store) *Server { server := &Server{store: store} router := gin.Default() if v, ok := binding.Validator.Engine().(*validator.Validate); ok { v.RegisterValidation("currency", validCurrency) } router.GET("/transfers/:id", server.getTransfer) router.POST("/transfers", server.createTransfer) server.router = router return server } 第二引数の関数部分へtrue or falseで返せるように実装します。 var listCurrencies = map[string]bool{ "USD": true, "EUR": true, "JPY": true, } var validCurrency validator.Func = func(fieldLevel validator.FieldLevel) bool { if currency, ok := fieldLevel.Field().Interface().(string); ok { return listCurrencies[currency] } return false } ロジックについては完了です。 最後にバリデーターを以下のように記載します。(binding:”required,currency”) 前述のAPI部分で触れた、バリデーターとして使用するタグ名はここで使用します。 type createAccountRequest struct { ...... Currency string `json:"currency" binding:"required,currency"` } 最後に 今回はGinについて触れましたが、Golangといえば、「net/http」ですよね。 今後はnet/httpについて触れていきたいなと思います。 では、また! The post Ginでカスタムバリデーションを実装してみた。 first appeared on miracleave Tech Blog .
アバター
はじめに お久しぶりです!新垣です!今回はVue.jsで新規作成したVue.jsアプリケーションがホットリロードされず、すぐ解消できるやろと舐めてかかったら積んだ話です! Vue.js で新規作成したアプリケーションがホットリロードされない? ある日のこと。業務の一環で作成したVue.jsプロジェクトが何故かホットリロードされない現象が発生しました。私もVue.jsに慣れてきた頃でGoogleで調べてみるとヒットする記事が多々出てきました。 【メモ】Vue CLI でホットリロードが効かない https://qiita.com/ntm718/items/6023b0063f78d53192a1 しかし、設定を行っているvue.config.jsを確認したところ、設定にdefineConfigを利用しており参考になりませんでした。 vue.config.js const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true }) defineConfig を使ってどのようにホットリロード設定するか 早速結論ですが以下のように設定するとホットリロードが上手く動作するようになります。 module.exports = defineConfig({ transpileDependencies: true, configureWebpack: { watch: true } }) 一応なぜこのような記述になるのかを解説しておきます。 ホットリロードというのはwebpackのwatchもしくはwatch optionを利用してファイルを監視し、変更があるたびビルドするように設定する必要があります。つまり、defineConfigを使ってwebpackのwatchの設定をオンにする必要があるということになります。 Watch and WatchOptions | webpack https://webpack.js.org/configuration/whttps://webpack.js.org/configuration/watch/atch/ defineConfig が定義されていたのは以下の場所になります。 node_modules\@vue\cli-service\types\index.d.ts 136行目 – 138行目 export { ProjectOptions, ServicePlugin, PluginAPI } type UserConfig = ProjectOptions | ConfigFunction export function defineConfig(config: UserConfig): UserConfig defineConfig の引数に型として宣言されているUserConfig は ProjectOptions もしくは ConfigFunction を型として受けます。ProjectOptionsはどのような定義がされているでしょうか。 node_modules\@vue\cli-service\types\ProjectOptions.d.ts 138行目 – 145行目 /** * A function that will receive an instance of `ChainableConfig` powered by [webpack-chain](https://github.com/mozilla-neutrino/webpack-chain) */ chainWebpack?: (config: ChainableWebpackConfig) => void; /** * Set webpack configuration. If the value is `Object`, will be merged into config. If value is `Function`, will receive current config as argument */ configureWebpack?: WebpackOptions | ((config: WebpackOptions) => (WebpackOptions | void)); ここでwebpackの設定を受けています。ここで型として定義されているWebpackOptionsの内容をさらに確認してみましょう。 node_modules\webpack\types.d.ts 2280行目 – 2284行目 /** /** * Enter watch mode, which rebuilds on file change. */ watch?: boolean; watchモードの設定が見つかりました。watch: プロパティをtrueにすることでホットリロードを実現することができそうです。 まとめ 今回defineConfigを使ったホットリロードの設定方法がなかなか見つからずvue内部のコードを参照する必要がでてきました。便利なフレームワークやライブラリ等を利用していると内部処理を理解せずエラー解消に時間がかかることがしばしばあります。フレームワークやライブラリに振り回されないようにこれからも精進しようと感じました。 The post Vue.js がホットリロードされなくて困った話 first appeared on miracleave Tech Blog .
アバター
はじめに 上野です! 今回はVercelにNext.jsのアプリをデプロイし、独自ドメインの紐付けまでを行っていきたいと思います! Vercelとは? VercelはNext.jsを開発しているVercel lnc.提供しているサーバーレスプラットフォームです。このVercelを利用することによって手軽にNext.jsのアプリケーションをデプロイすることができます。 また、サーバーレスプラットフォームになっているためサーバー側の設定もほぼ自動的に行ってくれて、SSL暗号化やキャッシュの無効化、圧縮による軽量化なども自動で行ってくれます! Next.jsのアプリを作成 今回はローカル環境を汚したくないためDockerを使用して開発環境を構築していきます。 まずは、作業するためのディレクトリを作成し、Dockerfileとdocker-compose.ymlを作成していきます。 mkdir nextapp cd nextapp touch Dockerfile docker-compose.yml docker-compose.yml version: '3' services: app: build: context: . dockerfile: Dockerfile volumes: - .:/app:cached ports: - 3434:3000 command: sh -c "npm run dev" Dockerfile FROM node:14.17.0 WORKDIR /app 次に下記のコマンドでNext.jsを構築します。 docker-compose run --rm app yarn create next-app --typescript 実行途中で「What is your project named?」と聞かれるので、「app」と入力し、Enterを押してください。 処理が完了すると以下のような画面が表示されるはずです。 現在のnextappフォルダ直下に「yarn create next-app」で作成したフォルダとファイルを移動します。 mv app/* app/.* app/.next . rm -r -app 以下コマンドを実行し、開発モードでサーバーを起動します。 docker-compose up -d 「 localhost:3434 」にアクセスすると作成されたアプリを確認することができます。 画面にアクセスできることが確認できたら、Githubにプッシュをしていきます。 Githubにログインし、リポジトリを作成します。 先程作成したNext.jsのアプリをリポジトリに反映します。 git init git add . git commit -m "create nextjs" git remote add origin https://github.com/{user_name}/nextapp.git git push -u origin main Vercelにデプロイ 以下URLよりVercelにログインすることができます。 URL: https://vercel.com/login 私はGithubアカウントがあるので、Githubアカウントでログインします。 「New Project」ボタンをクリックし、新しいプロジェクトを作成します。 「Add Github Account」ボタンをクリックします。 ボタンをクリックすると以下の画面が表示されるので、「Only select repositories」を選択し、先程作成したリポジトリを選択してください。 リポジトリ選択後、「install」ボタンをクリックし、パスワードを入力してください。 Githubとの連携がうまくいくと以下のような画面が表示されます。 次に「import」ボタンをクリックし、プロジェクトの設定画面に移動します。 プロジェクト設定では、デフォルトのままで良いのでそのまま「Deploy」ボタンをクリックします。 デプロイが無事完了すると以下のような画面が表示されます。 「Go to Dashboard」をクリックし、実際にデプロイできているかDomainにアクセスして確認してみてください。 ドメイン取得・紐付け 今回は無料ドメインを使用したいので、以下のfreenomでドメインを取得していきます。 https://www.freenom.com/ja/freeandpaiddomains.html 以下ページからログインをします。 https://my.freenom.com/clientarea.php ログインが完了したら、「Services>Register a New Domain」をクリックし、新しいドメインを取得する画面に移動します。 nextappという名前でドメインを取得したいので、検索欄に「nextapp」と入力し、取得できるドメインを選択してください。 今回は「nextapp.tk」というドメイン名でドメインを取得するので、「.tk」を選択し「checkout」ボタンをクリックしてください。 特に設定は変更せず、「Continue」ボタンをクリックし次に進んでください。 チェックボックスにチェックを付け、「Complete Order」ボタンをクリックし注文を完了させてください。 注文完了後は、ホーム画面に戻りメニューバーの「Services>My Domains」をクリックしてください。 以下の画面が表示されるので、先程取得したドメインの「Manage Domain」ボタンをクリックし、ドメインの設定画面に移動します。 以下の画面から「Management Tools>Nameservers」を選択し、さらに「 Use custom nameservers (enter below) 」を選択し、ネームサーバーが記入できる状態にしてください。 次にVercelの画面に移動し、先程作成したドメインを登録していきます。 作成が完了するとネームサーバーが正しくないというエラーが発生しているので解消していきます。 以下の矢印の箇所にあるネームサーバー名を先程のfreenomのネームサーバーを設定する画面に入力し、「Change Nameservers」ボタンをクリックします。 早ければ数秒で先程表示されていたネームサーバーのエラーが消えるはずです。 エラーが完全に消えた状態で以下URLにアクセスするとNext.jsのトップページが表示されるはずです。 ※トップページが表示されない場合、SSL化の反映に時間がかかっているかもしれないので数分待っていただく必要があります。 URL: https://www.nextapp.tk 無事、独自ドメインでNext.jsのアプリを表示する事ができました!! まとめ 初めてVercelを使用したのですが、思った以上に簡単にデプロイできて驚きました!また、VercelではAWSのRDSやDynamoDB、Firebaseなど他のクラウドサービスのDBとも連携できるようなので、今後アプリ開発をする際はVercelも候補に入れてみようと思います! 今回は以上となります。ありがとうございました! The post Vercelにデプロイしたアプリに独自ドメインを紐づけてみた first appeared on miracleave Tech Blog .
アバター
補完って便利だよね! はじめに 積みゲーが多すぎていろいろと中途半端になっている Bell です。 今回は cli のシェルを使っているときに利用している Tab キーの補完についてです。 当たり前のように使いつつどう実装されているのか知らなかったので少し調べてみました。 前提条件 久しぶりのコマンドって使い方を忘れてしまいますよね。 想定として普段は自動でリリースされるシステムを何らかの理由で手動で行ったと仮定しましょう。 release コマンドの補完を設定してみます。 やってみよう まずは release コマンドを作成します。 touch /usr/local/bin/release chmod 755 /usr/local/bin/release 今回は実行する必要はないので中身は空で OK です。 次に /etc/bash_completion.d/ に補完の設定を保存しましょう。 touch /etc/bash_completion.d/release 内容はひとまずこちら __release_bash_completion() { echo echo 'update build deploy restart' } complete -F __release_bash_completion release これで設定ファイルを読み込みタブ補完をすると echo のメッセージが確認できるかと思います。 source /etc/bash_completion.d/release release <Tab> <Tab> 補完で設定された関数が動いてますね、シンプルです。 ですがプロンプトが戻ってきていませんね。 COMPREPLY 変数にメッセージを渡すよう修正しましょう。 ついでにサブコマンドの階層構造も持たせてみましょう。 __release_bash_completion() { case "$3" in release) message="update build deploy restart";; build) message="test package";; esac COMPREPLY=(${message}) } complete -F __release_bash_completion release 分岐を作ってマッチングしているだけなのでほとんど普通のシェルスクリプトですね。 第三引数には入力し終えた値が入っています。 コマンド名だけの場合にはコマンド名の release を、release update などサブコマンドを入力した場合にはその値が入ってきます。 これを利用して入力されているサブコマンドに応じたメッセージの表示が可能です。 例の場合では update deploy restart が入力されている場合はそのまま表示しますが、 build の場合だけはテストを実行するのかパッケージを作成するのか選べるようなイメージです。 第二引数には入力途中の値が入っています。 前方一致の候補を出力する場合なんかに利用できますね。 これでお手製コマンドにも補完が表示できるわけなのですが、内部で使うだけなら usage をきちんと書いておくほうが効果的だったり・・・? サブコマンドの階層が深かったり、オプションが多い場合はありがたいかもですね! ではでは。 The post bash-completion でコマンド補完を設定する first appeared on miracleave Tech Blog .
アバター
はじめに ローカル汚したくないマンの佐々木です。 しばらくDockerを使っていながらも、インテリセンスを効かせるためであったり、デバッグするためにローカルにnodeを入れたり、pythonを入れたりしていました。 これってDocker使ってる意味薄くない?と思いつつ、作業は進めないといけない…という葛藤の中、ずるずるとここまできてしまっていました。 今回は、開発に必要なものは全てコンテナに入れ込んで、クリーンなローカル環境を維持する VSCode Remote Containers を活用した開発環境のご紹介をしていきたいと思います。 筆者の環境 Windows11 WSL2 (Ubuntu 20.04) DockerDesktop 4.5.1 Docker 20.10.12 ※弊社メンバーが利用しているM1 Macbook Airでも動作しています。 対象者 Dockerを使っている方 JavaScript/TypeScript/Java/Golang/Ruby/Pythonなど各プログラム言語利用者 VSCodeを普段利用している方 必要なもの Docker Desktop Visual Studio Code 全体像 公式サイトに記載の内容がわかりやすいと思いますので引用させていただきます。 Developing inside a Container using Visual Studio Code Remote Development Developing inside a Container using Visual Studio Code Remote Development code.visualstudio.com ローカル環境に用意するものは、Docker / VS Codeのみとなっており、各言語に必要なものは全てコンテナ内にインストールしていくことで、どんな環境でも対応していけると思います。 前準備 (Windowsのみ) WSL2 インストール DockerDesktop インストール VisualStudioCode インストール 環境構築 Next.jsを最近利用する機会が増えており、Next.jsの環境を作っていきたいと思います。 VSCodeの拡張機能の Remote – Containers をインストールしていきます。 Remote - Containers - Visual Studio Marketplace Extension for Visual Studio Code - Open any folder or repository inside a Docker container and take advantage of Visual Studio Code's full feature set. marketplace.visualstudio.com 左側のメニューに次のアイコンが表示されればインストール完了です。 早速、リモートコンテナに接続してみます。 ①Explorerを開きます。 ②OpenFolderでリモートコンテナとして開きたい対象のフォルダを選択します。 ③リモートコンテナを立ち上げます。 初回リモートコンテナ接続時は、 Add Development Container Configuration Files... を利用します。 WSLの場合 WSLを利用している方は、リモートコンテナに接続する前にWSL内に接続する必要があります。 次の拡張機能をインストールする必要があります。 Remote - WSL - Visual Studio Marketplace Extension for Visual Studio Code - Open any folder in the Windows Subsystem for Linux (WSL) and take advantage of Visual Studio Code's full feature set. marketplace.visualstudio.com Open Folder in WSL... よりWSL内の対象フォルダを選択してください。 WSL内のファイルを表示している場合は以下のような表示になります。 F1 を押下し、コマンドパレットで Remote-containers: Add Development Container Configuration Files を選択します。 開発環境として利用したいコンテナを選択してください。 もし存在しない場合は、 Show All Deifinitions... より用意されているコンテナ全てを確認することができます。 利用できるコンテナはこちらを参考にしてください。 vscode-dev-containers/containers at main · microsoft/vscode-dev-containers A repository of development container definitions for the VS Code Remote - Containers extension and GitHub Codespaces - vscode-dev-containers/containers at main... github.com 今回は、Node.js & TypeScriptコンテナを利用していきます。 Node.jsのバージョンを選択します。 bullseye/busterなどはdebianのバージョンになります。 bullseye: debian11 buster: debian10 他にインストールしたいものを選択します。 今回はGitをインストールします。 .devcontainer フォルダ、ファイル群が作成されます。 左下の緑部分をクリックすることでリモートコンテナ関連のメニューを表示させ、 Reopen In Container を選択します。 コンテナイメージのダウンロードやビルドを行うので、少し時間がかかります。 左下に Dev Container が表示されれば、コンテナ内をVSCodeで表示している状態となります。 Node.js&TypeScriptイメージを選択しているので、Next.jsの構築を行っていきたいと思います。 Next.jsセットアップ 次の手順を元にセットアップしていきます。 Getting Started | Next.js Get started with Next.js in the official documentation, and learn more about all our features! nextjs.org まずは create-next-app で土台を作ります。 npx create-next-app@latest --typescript c reate-next-app が完了しましたら、 npm run dev を実行し、起動してみます。 node ➜ /workspaces/remote-container $ npm run dev npm ERR! code ENOENT npm ERR! syscall open npm ERR! path /workspaces/remote-container/package.json npm ERR! errno -2 npm ERR! enoent ENOENT: no such file or directory, open '/workspaces/remote-container/package.json' npm ERR! enoent This is related to npm not being able to find a file. npm ERR! enoent npm ERR! A complete log of this run can be found in: npm ERR! /home/node/.npm/_logs/2022-02-22T04_57_54_135Z-debug.log ターミナルのフォルダ位置が悪かったので、移動してから再度、実行してみます。 node ➜ /workspaces/remote-container $ cd my-app/ node ➜ /workspaces/remote-container/my-app (main) $ npm run dev > my-app@0.1.0 dev > next dev ready - started server on 0.0.0.0:3000, url: http://localhost:3000 event - compiled client and server successfully in 1962 ms (124 modules) Attention: Next.js now collects completely anonymous telemetry regarding usage. This information is used to shape Next.js' roadmap and prioritize features. You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL: https://nextjs.org/telemetry 無事にNext.jsの起動が行えました。 http://localhost:3000が自動でフォワードされますので、実際にアクセスして起動確認を行います。 ポート状態については、上部メニューの View > Open View... > Ports から確認できます。 まとめ VSCodeのRemoteContainerを利用し、クリーンな環境について紹介させていただきました。 ローカルに様々なバージョンのNodeを管理するためにnodistやnを入れる必要もなくなり、クリーンって素晴らしい。と思う今日この頃です。 複数のプロジェクトを行う際も、このAプロジェクトではJava11だけど、BプロジェクトではJava8みたいな場面もあり、最初からコンテナで環境構築するようにしています。 今回紹介できませんでしたが、devcontainer.jsonファイルに拡張機能の設定を記述することでリモートコンテナ立ち上げ時に拡張機能をインストールしたりなどもできますので、チーム内の開発環境を統一することもできます。 こちらについては、また別の機会にでもご紹介できればと思います。 クリーンな環境でストレスレスな開発に勤しみましょう🐻 The post VSCode開発言語共通な開発環境のススメ first appeared on miracleave Tech Blog .
アバター