Rubyと筋肉とギターとわたし

筋トレが仕事です

【vim】vim-prettierがPrettier: failed to parse bufferで動かないときに確認すること

f:id:rdwbocungelt5:20200320131618p:plain

どうもてぃ。

vim + JavaScript/TypeScriptで悩まされる日々が続いておりました。

が、とうとう決着をつけることが出来て感謝カンゲキ雨嵐です(何に?)

何がおきていたか

当方、vimmmmmmmmmmmmmmmmmmmmmerです。

フロントエンド開発で重要になってくるprettierのプラグインvim-prettierを使用しているのですが、いかんせん上手く動かない。

題名どおりのエラーがjs/jsx/tsファイルで起き、prettierが動かないという自体が起きてました(tsxファイルだと何故か動く)。

代替えとしてdense-analysis/aleを使用している人がいたりいなかったり。

自分の方でもaleを使用してprettierを掛けるのですが、以下のように楽しいことになります。

f:id:rdwbocungelt5:20210826205030g:plain

これがマジでムカつく。

最近は色んな所でjsを触る機会が増えてきたため、いい加減直そうと思い調査に走りました。

調査

同じように悩んでる方がたくさんいるようでした。

github.com

github.com

github.com

issue内で書かれてるものを自分の方で試してみても全く効果はなかったのでライブラリのオプションを確認していくことに。

試したこと その1

prettierの入っているプロジェクト内で:PrettierCliPathで何が出てくるか確認。

これに関してはprettierと返ってきたのでプロジェクト内のprettierが実行されてるっぽい。

もしかしたら、vim-prettierのバグでプロジェクト内のprettierを認識できてない可能性がある…ということでglobalで使用しているprettierを使うようにオプション指定。

.vimrcに下記を追加。

# which prettierで出てきたやつを指定
let g:prettier#exec_cmd_path = '/home/motty/.asdf/shims/prettier'

:PrettierCliPathで上記パスが出てきたけど、:Prettierは上手くいかない…。

予想が外れたので次。

試したこと その2

これが正解でした。

何事も解決は公式が書いてるだろうと、vim-prettier本体のドキュメントを見に行くことに。

ローカルにあるprettier.txtを確認(自分のパスは/home/motty/.cache/dein/repos/github.com/prettier/vim-prettier/docでした)

と、自分の.vimrcで設定してある項目に目がつきました。

  " flow|babylon|typescript|css|less|scss|json|graphql|markdown or empty string
  " (let prettier choose).
  " default: ''
  let g:prettier#config#parser = ''

自分はparserをbabylonを指定してました。以前、vim-prettierが動かなかったときにbabylonを指定したら上手く行ったからです。かなり安直な考え。

もしや…と思いdefault状態に戻すことに。以下自分のvim-prettier設定です。

  " vim-prettier
  call dein#add('prettier/vim-prettier', {
       \ 'do': 'yarn install',
       \ 'branch': 'release/1.x',
       \ 'for': [
       \ 'javascript',
       \ 'typescript',
       \ 'typescriptreact',
       \ 'css',
       \ 'less',
       \ 'scss',
       \ 'json',
       \ 'graphql',
       \ 'vue',
       \ 'yaml',
       \ 'html'
       \]})
  let g:prettier#autoformat = 0
  let g:prettier#quickfix_enabled = 0
  let g:prettier#config#semi = 'false'
  let g:prettier#config#single_quote = 'true'
  let g:prettier#config#bracket_spacing = 'true'
  let g:prettier#config#parser = ''
  let g:prettier#exec_cmd_path = '/home/motty/.asdf/shims/prettier'
  " autocmd BufWritePre *.js,*.jsx,*.ts,*.tsx,*.vue,*.css,*.scss,*.json PrettierAsync

:Prettierを動かすと…


























うまくいったぁぁぁああああああああああああああああああああああああああああああああああ

つーか、babylonのリポジトリはすでに4年前に開発終了してましたね。

github.com

babelのモノレポ化前の名残らしく、今は@babel/parserとして開発されているそうです。そら動かんわ。

終わり

色んなものを定期的にメンテナンスしましょう。

今年中にvimrcの整理/toml化へ向けてがんばります。ALEのムカつく問題も残ってますしね。

【Golang】文字列が大文字かどうかの判定

f:id:rdwbocungelt5:20200720165613p:plain

どうもてぃ。

atcoder 第171回のA問題ででてきたので備忘録として。

atcoder.jp

TL;DR

package main

import (
    "fmt"
    "unicode"
)

func main() {
    var a string
    fmt.Scan(&a)
    r := rune(a[0])

    if unicode.IsUpper(r) {
        fmt.Println("A")
    } else {
        fmt.Println("a")
    }
}

詰まった点

値をrune型で受け取って上記のunicode.IsUpper()へ渡すと全てfalseになっちゃう

使ったpackage

func IsUpper(r rune) boolです。小文字を判定するなら func IsLower(r rune) boolがある。

pkg.go.dev

終わり

割とatcoderのA, B問題は言語の基礎を試すのにかなりいいですね。

知らないものがでてきたら今後もクソ記事書いていきたいと思います。

【Golang】mapを構造体へ割当(変換)する

どうもてぃ。

前回のつづきで備忘録。

smot93516.hatenablog.jp

mapと構造体を準備

type Addr struct {
    PostalCode int
    Country    string
}

type User struct {
    Name    string
    Age     int
    Address Addr
    Email   string
}

user := map[string]interface{}{
    "Name": "motty",
    "Age":  30,
    "Address": map[string]interface{}{
        "PostalCode": 1234567,
        "Country":    "japan",
    },
    "Email": "test@gmail.com",
}

構造体で定義した型に合わせてmapを作成してます。

map側の型と構造体の型が違った場合どうなるか…というのは後ほどやっていきます。

mapをjsonへ変換

encoding/jsonjson.Marshalを使用。

// mapをjsonへ変換
byte, err := json.Marshal(user)
if err != nil {
    fmt.Println(err)
    return
}
fmt.Println(string(byte))

こんな感じ。string(byte)の結果は以下。

{"Address":{"Country":"japan","PostalCode":1234567},"Age":30,"Email":"test@gmail.com","Name":"motty"}

json.Marshalの定義元を確認すると、

func Marshal(v interface{}) ([]byte, error) {
    e := newEncodeState()

    err := e.marshal(v, encOpts{escapeHTML: true})
    if err != nil {
        return nil, err
    }
    buf := append([]byte(nil), e.Bytes()...)

    encodeStatePool.Put(e)

    return buf, nil
}

と、最終的には[]byteerrorが返ってるので

byte, err := json.Marshal(user)

のようにerrも受け取って上げる必要がある。

とりあえず検証するだけ、エラーハンドリング不要であれば

byte, _ := json.Marshal(user)

として省くことも出来きます。

jsonを構造体へ

encoding/jsonjson.Unmarshalを使用。

// jsonを構造体へ割当
var u User
if err := json.Unmarshal(byte, &u); err != nil {
    fmt.Println(err)
    return
}

fmt.Println(u)

json.Unmarshalの定義元を確認。

func Unmarshal(data []byte, v interface{}) error {
    // Check for well-formedness.
    // Avoids filling out half a data structure
    // before discovering a JSON syntax error.
    var d decodeState
    err := checkValid(data, &d.scan)
    if err != nil {
        return err
    }

    d.init(data)
    return d.unmarshal(v)
}

Unmarshalの場合errorのみ返ってきて、第二引数に渡したポインタアドレスに第一引数で渡した値がスキャニングされてるっぽい(辿っていったけど詳しくはわからん)。

全部合わせたやつ

package main

import (
    "encoding/json"
    "fmt"
)

type Addr struct {
    PostalCode int
    Country    string
}

type User struct {
    Name    string
    Age     int
    Address Addr
    Email   string
}

func main() {
    user := map[string]interface{}{
        "Name": "motty",
        "Age":  30,
        "Address": map[string]interface{}{
            "PostalCode": 1234567,
            "Country":    "japan",
        },
        "Email": "test@gmail.com",
    }

    // mapをjsonへ変換
    byte, err := json.Marshal(user)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(byte))

    // jsonをstructへ割当
    var u User
    if err := json.Unmarshal(byte, &u); err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(u)
}

実行結果

{"Address":{"Country":"japan","PostalCode":1234567},"Age":30,"Email":"test@gmail.com","Name":"motty"}
map[Address:map[Country:japan PostalCode:1234567] Age:30 Email:test@gmail.com Name:motty]

これをある程度理解していれば、簡単なwebサーバーは作れる。

サーバーを作るのはまた次回にするとして…

mapの型と構造体の型が異なる場合

たとえば以下のように構造体UserのAgeがint型であるにもかかわらず、map側がstringになっていた場合。

type Addr struct {
    PostalCode int
    Country    string
}

type User struct {
    Name    string
    Age     int
    Address Addr
    Email   string
}

user := map[string]interface{}{
    "Name": "motty",
    "Age":  "30",
    "Address": map[string]interface{}{
        "PostalCode": 1234567,
        "Country":    "japan",
    },
    "Email": "test@gmail.com",
}

何が起きるか、実行してみると

{"Address":{"Country":"japan","PostalCode":1234567},"Age":"30","Email":"test@gmail.com","Name":"motty"}
json: cannot unmarshal string into Go struct field User.Age of type int

jsonを構造体へ割当の部分、つまりjson.Unmarshal部分でエラーが返ってきます(しっかりしてんな)。

やってくるリクエスト(map)に対して構造体をしっかり用意してあげないと、今回のようにエラーが返ってくる…といった感じです。

mapと構造体でkey, valueの数が異なる場合

構造体で用意してある型とmapの型は一致しているが、mapの中身の要素数が異なる時どうなるか。

例えば、mapに"Test": "test"というkey, valueが入っていたとき

type Addr struct {
    PostalCode int
    Country    string
}

type User struct {
    Name    string
    Age     int
    Address Addr
    Email   string
}

user := map[string]interface{}{
    "Name": "motty",
    "Age":  30,
    "Address": map[string]interface{}{
        "PostalCode": 1234567,
        "Country":    "japan",
    },
    "Email": "test@gmail.com",
    "Test": "test",
}

こういう場合、最終的にはエラーは出ず構造体側が優先されます。

json.Unmarshalされたとき、構造体にないものは割り当てられないという認識でいると良いです。

もし"Test"を受け取りたいなら、構造体側にちゃんと型を定義してあげましょう。

最後に

Goは楽しいですよ。

次回は構造体→mapを書こうと思います。

【Golang】mapを作る

どうもてぃ。

ルビーやジャヴァスクリプトをやっていると毎回間違えるのでメモ。

対応関係

言語
ruby hash = {}
go map := map[string]interface{}
js object = {}

大まかに分類するとこんな感じ。

ただ、goの場合型を指定するため、対応する型以外はmapの中に入れられないです。rubyやjsのように動的にしたいなら…

map := map[interface{}]interface{}{}

と宣言すればよいですが、これだと型の恩恵がないのでほとんど使わない。

goやってるのならgoしましょう。

宣言方法

空のmap

// その1(明示的に書く)
var hoge map[string]int = map[string]int{}

// その2(makeを使う)
huga := make(map[string]int)
huga2 := make(map[string]int, 10) // 領域を確保して作成可能

// その3(簡略化)
hogo := map[string]int{}

そもそもmapを作成するのであれば、ある程度中身の構造が決まっているので構造体を使用することが多い。

が、その中でもその3の使用率が高いですね。

中身ありのmap

上とほとんど一緒かも。

// 明示的に
var foo map[string]string = map[string]string{"go": "hello echo", "ruby": "hello rails", "javascript": "hello react"}

// 簡略
bar := map[string]string{"go": "hello echo", "ruby": "hello rails", "javascript": "hello react"}

// 上の改行版
yoo := map[string]string{
  "go": "hello echo",
  "ruby": "hello rails",
  "javascript": "hello react",
}

// キー指定の代入
hash := make(map[string]string)
hash["go"] = "hello echo"
hash["ruby"] = "hello rails"
hash["javascript"] = "hello react"

最後に

もう忘れない(たぶん)。

次はmap[string]interface{}から構造体へ変換、またその逆もまとめる予定。

【備忘録】Next.js × TypeScriptのセットアップメモ

どうもてぃ。

毎回eslint/prettierの設定をやり直すのクソだるいのでメモしときます。

環境

  • node v15.12.0

プロジェクト作成

next v11だと標準でeslintの設定が入るみたいですね。メチャ楽になったな。

# 自分はこれだとeslintが入らなかった…
$ npx create-next-app <project-name> --typescript

or

$ yarn create next-app <project-name> --typescript

package.jsonの中身がこんな感じ。

{
  "name": "project-name",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "11.0.1",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "devDependencies": {
    "@types/react": "17.0.14",
    "eslint": "7.31.0",
    "eslint-config-next": "11.0.1",
    "typescript": "4.3.5"
  }
}

GitHub Repositoryの作成

これはおまけ

$ gh repo create <project-name>

github cliが入ってないと無理なのでご注意を。

tsconfig.jsonの設定

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "noImplicitAny": true,
    "baseUrl": "./",
    "paths": {
      "~/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

noImplicitAny, baseUrl, paths を追加しただけ。

eslint設定

package.jsonみたらわかりますが、最初から入ってます。

.eslintrcの中身がこれ。

{
  "extends": ["next", "next/core-web-vitals"]
}

prettier設定

.prettierrc.jsをプロジェクトルートに作成。

module.exports = {
  jsxBracketSameLine: true,
  singleQuote: true,
  trailingComma: 'all',
  semi: false,
  printWidth: 110,
  tabWidth: 2,
  parser: 'typescript',
  overrides: [
    {
      files: '*.json',
      options: {
        parser: 'json',
      },
    },
  ],
}

その他必要なもの

babel-plugin-module-resolver導入

$ yarn add -D babel-plugin-module-resolver

babelrc.jsをプロジェクトルートに作成

module.exports = {
  presets: ['next/babel'],
  plugins: [
    [
      'module-resolver',
      {
        root: ['.'],
        alias: {
          '~': './',
        },
      },
    ],
  ],
}

autoprefixer導入

$ yarn add autoprefixer

axios導入

$ yarn add axios

ReduxToolkit導入

state管理はReduxToolkitを使用。

$ yarn add @reduxjs/toolkit

終わり

最終的な package.jsonがこちら。

{
  "name": "microcms-next-blog",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@reduxjs/toolkit": "^1.6.1",
    "autoprefixer": "^10.3.1",
    "axios": "^0.21.1",
    "next": "11.0.1",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "devDependencies": {
    "@types/react": "17.0.14",
    "babel-plugin-module-resolver": "^4.1.0",
    "eslint": "7.31.0",
    "eslint-config-next": "11.0.1",
    "typescript": "4.3.5"
  }
}
$ yarn dev

f:id:rdwbocungelt5:20210718141058p:plain

【docker】alpine ruby imageでgccが入っているのにstdio.h: No such file or directoryになる

f:id:rdwbocungelt5:20180816164743p:plain

どうもてぃ。お久しぶりです。

今日も解決記事です。

ゆっくりしていってね

環境

  • ruby 3.0.0 (alpine image)
  • PostgreSQL v12 (alpine image)
  • rails 6.1.4
  • Docker version 20.10.2, build 20.10.2-0ubuntu1~18.04.2
  • docker-compose version 1.27.4, build 40524192
% cat /ext/lsb-release

DISTRIB_ID=LinuxMint
DISTRIB_RELEASE=19.3
DISTRIB_CODENAME=tricia
DISTRIB_DESCRIPTION="Linux Mint 19.3 Tricia"

一応Dockerfile

FROM ruby:3.0.0-alpine

ENV RUNTIME_PACKAGES="linux-headers build-base libxml2-dev libxslt-dev make gcc libc-dev nodejs yarn tzdata g++ postgresql-dev postgresql git openssl less curl unzip" \
    CHROME_PACKAGES="chromium-chromedriver zlib-dev chromium xvfb wait4ports xorg-server dbus ttf-freefont mesa-dri-swrast udev" \
    DELETE_PACKAGES="build-packages libxml2-dev make gcc curl-dev libc-dev g++ curl unzip" \
    BUILD_PACKAGES="build-base curl-dev" \
    APP_ROOT="/myapp" \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo

RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --no-cache ${CHROME_PACKAGES} && \
    apk add --virtual build-packages --no-cache ${BUILD_PACKAGES} && \
    gem install rails -v '6.1.4' && \
    gem install bundler

WORKDIR /tmp
COPY Gemfile* ./

RUN curl -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip && \
    unzip NotoSansCJKjp-hinted.zip -d . && \
    mkdir -p /usr/share/fonts/noto && \
    cp ./*.otf /usr/share/fonts/noto/ && \
    chmod 644 /usr/share/fonts/noto/*.otf && \
    fc-cache -fv

RUN bundle install -j4 && \
    bundle clean --force && \
    apk del ${DELETE_PACKAGES} ${BUILD_PACKAGES} && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    rm -rf /usr/local/share/.cache/* /var/cache/* /tmp/*

WORKDIR $APP_ROOT
COPY . ${APP_ROOT}/

CMD ["rails", "server", "-b", "0.0.0.0"]

fontを入れてるのはスクレイピング用です。

他は何の変哲もない(自分では)しっかりと作り込んだ(つもりでいた)愛を全力で注ぎ込んだDockerfileです。

やりたかったこと

  • rails apiモードで環境構築
  • どうせなら今まで使ってたDockerfileとdocker-compose.ymlを利用する(ruby alpineベース)

上記2点。

今まで別のプロジェクトでも何の問題もなく動いていたので、Dockerfiledocker-compose.ymlrails apiモード用にちょっといじってビルドしました。

事件はおこった

その1: gemのコンパイルgccとmakeが必要だった

まあ、よくあるやつ。

これはDockerfileのDELETE_PACKAGEから除外すれば直りました。

修正したDockerfileが以下。

FROM ruby:3.0.0-alpine

# don't delete make and gcc
ENV RUNTIME_PACKAGES="linux-headers build-base libxml2-dev libxslt-dev make gcc libc-dev nodejs yarn tzdata g++ postgresql-dev postgresql git openssl less curl unzip" \
    CHROME_PACKAGES="chromium-chromedriver zlib-dev chromium xvfb wait4ports xorg-server dbus ttf-freefont mesa-dri-swrast udev" \
    DELETE_PACKAGES="build-packages libxml2-dev curl-dev libc-dev g++ curl unzip" \
    BUILD_PACKAGES="build-base curl-dev" \
    APP_ROOT="/myapp" \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo

RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --no-cache ${CHROME_PACKAGES} && \
    apk add --virtual build-packages --no-cache ${BUILD_PACKAGES} && \
    gem install rails -v '6.1.4' && \
    gem install bundler

WORKDIR /tmp
COPY Gemfile* ./

RUN curl -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip && \
    unzip NotoSansCJKjp-hinted.zip -d . && \
    mkdir -p /usr/share/fonts/noto && \
    cp ./*.otf /usr/share/fonts/noto/ && \
    chmod 644 /usr/share/fonts/noto/*.otf && \
    fc-cache -fv

RUN bundle install -j4 && \
    bundle clean --force && \
    apk del ${DELETE_PACKAGES} ${BUILD_PACKAGES} && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    rm -rf /usr/local/share/.cache/* /var/cache/* /tmp/*

WORKDIR $APP_ROOT
COPY . ${APP_ROOT}/

CMD ["rails", "server", "-b", "0.0.0.0"]

その2: msgpackのインストールでコケる

まずはエラーメッセージ。

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /usr/local/bundle/gems/msgpack-1.4.2/ext/msgpack
/usr/local/bin/ruby -I /usr/local/lib/ruby/3.0.0 -r ./siteconf20210630-1-7hbtci.rb extconf.rb
checking for ruby/st.h... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/usr/local/bin/$(RUBY_BASE_NAME)
    --with-ruby-dir
    --without-ruby-dir
    --with-ruby-include
    --without-ruby-include=${ruby-dir}/include
    --with-ruby-lib
    --without-ruby-lib=${ruby-dir}/lib
/usr/local/lib/ruby/3.0.0/mkmf.rb:471:in `try_do': The compiler failed to generate an executable file. (RuntimeError)
You have to install development tools first.
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:613:in `try_cpp'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:1124:in `block in have_header'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:971:in `block in checking_for'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:361:in `block (2 levels) in postpone'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:331:in `open'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:361:in `block in postpone'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:331:in `open'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:357:in `postpone'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:970:in `checking_for'
    from /usr/local/lib/ruby/3.0.0/mkmf.rb:1123:in `have_header'
    from extconf.rb:3:in `<main>'

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /usr/local/bundle/extensions/x86_64-linux-musl/3.0.0/msgpack-1.4.2/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /usr/local/bundle/gems/msgpack-1.4.2 for inspection.
Results logged to /usr/local/bundle/extensions/x86_64-linux-musl/3.0.0/msgpack-1.4.2/gem_make.out

An error occurred while installing msgpack (1.4.2), and Bundler cannot continue.
Make sure that `gem install msgpack -v '1.4.2' --source 'https://rubygems.org/'` succeeds before bundling.

In Gemfile:
  bootsnap was resolved to 1.7.5, which depends on
    msgpack

例のごとく最後に書いてる gem install msgpack -v '1.4.2' --source 'https://rubygems.org/' を実行してもダメ。

なのでmkmf.logを確認(docker containerに入ってから実行してます)。

/myapp # cat /usr/local/bundle/extensions/x86_64-linux-musl/3.0.0/msgpack-1.4.2/mkmf.log

"gcc -o conftest -I/usr/local/include/ruby-3.0.0/x86_64-linux-musl -I/usr/local/include/ruby-3.0.0/ruby/backward -I/usr/local/include/ruby-3.0.0 -I.    -O3 -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable  -fPIC conftest.c  -L. -L/usr/local/lib -Wl,-rpath,/usr/local/lib -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic     -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lruby  -lm   -lc"
In file included from /usr/local/include/ruby-3.0.0/ruby/ruby.h:23,
                 from /usr/local/include/ruby-3.0.0/ruby.h:38,
                 from conftest.c:1:
/usr/local/include/ruby-3.0.0/ruby/defines.h:16:10: fatal error: stdio.h: No such file or directory
   16 | #include <stdio.h>
      |          ^~~~~~~~~
compilation terminated.
checked program was:
/* begin */
1: #include "ruby.h"
2: 
3: int main(int argc, char **argv)
4: {
5:   return !!argv[argc];
6: }
/* end */

あれー?gccを最後削除するパッケージ対象から外してるんすけど。意味わからん。

msgpack関連をめちゃくちゃ検索してましたが、もしかしたらOSに何かあったのかもと思い、「docker alpine gcc stdio.h~~~~」みたいな感じで検索したら原因発覚。

alpineのgccとlibc-devの依存関係が非推奨で無くなってしまったとのこと。

別の記事では(ちょっとリンク忘れちゃった)、代わりに musl-dev入れたら最強になれるみたいなこと書いてました。

gitlab.alpinelinux.org

なので更に修正したDockerfileが以下。

FROM ruby:3.0.0-alpine

# don't delete make and gcc
ENV RUNTIME_PACKAGES="linux-headers build-base libxml2-dev libxslt-dev make gcc musl-dev nodejs yarn tzdata g++ postgresql-dev postgresql git openssl less curl unzip" \
    CHROME_PACKAGES="chromium-chromedriver zlib-dev chromium xvfb wait4ports xorg-server dbus ttf-freefont mesa-dri-swrast udev" \
    DELETE_PACKAGES="build-packages libxml2-dev curl-dev libc-dev g++ curl unzip" \
    BUILD_PACKAGES="build-base curl-dev" \
    APP_ROOT="/myapp" \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo

RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --no-cache ${CHROME_PACKAGES} && \
    apk add --virtual build-packages --no-cache ${BUILD_PACKAGES} && \
    gem install rails -v '6.1.4' && \
    gem install bundler

WORKDIR /tmp
COPY Gemfile* ./

RUN curl -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip && \
    unzip NotoSansCJKjp-hinted.zip -d . && \
    mkdir -p /usr/share/fonts/noto && \
    cp ./*.otf /usr/share/fonts/noto/ && \
    chmod 644 /usr/share/fonts/noto/*.otf && \
    fc-cache -fv

RUN bundle install -j4 && \
    bundle clean --force && \
    apk del ${DELETE_PACKAGES} ${BUILD_PACKAGES} && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    rm -rf /usr/local/share/.cache/* /var/cache/* /tmp/*

WORKDIR $APP_ROOT
COPY . ${APP_ROOT}/

CMD ["rails", "server", "-b", "0.0.0.0"]

libc-devを外してmusl-devを入れてます。

以上で無事ビルドとbundle installが通りましたとさ。

おわり

alpineイメージを使うメリットあまり感じてないので、slim-busterか普通のやつに変えようかなと模索中。

前見たときgithub actionで使うimageにalpineなかったからなー(もしかしたらもうあるかも)。

docker-compose v3.8のキャッチアップと一緒にプロジェクトのimage見直しやってみようと思います。

おつでした。

【asdf】ruby-build: definition not foundで指定バージョンのrubyをインストールできないとき

どうもてぃ。

記事がなかったのでメモとして。

環境

% cat /etc/lsb-release 
DISTRIB_ID=LinuxMint
DISTRIB_RELEASE=19.3
DISTRIB_CODENAME=tricia
DISTRIB_DESCRIPTION="Linux Mint 19.3 Tricia"

asdf install ruby

% asdf install ruby 2.7.3
Downloading ruby-build...
Cloning into '/home/motty/.asdf/plugins/ruby/ruby-build-source'...
remote: Enumerating objects: 11536, done.
remote: Counting objects: 100% (229/229), done.
remote: Compressing objects: 100% (113/113), done.
remote: Total 11536 (delta 174), reused 151 (delta 105), pack-reused 11307
Receiving objects: 100% (11536/11536), 2.44 MiB | 12.60 MiB/s, done.
Resolving deltas: 100% (7641/7641), done.
Note: checking out 'v20201005'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 29d1749 bump version to 20201005
ruby-build: definition not found: 2.7.3

むむ!?

ローカルにあるインストール候補を確認してみる。

% asdf list-all ruby | grep 2.7
1.8.6-p287
2.0.0-p247
2.2.7
2.7.0-dev
2.7.0-preview1
2.7.0-preview2
2.7.0-preview3
2.7.0-rc1
2.7.0-rc2
2.7.0
2.7.1
2.7.2
jruby-9.2.7.0
rbx-2.2.7
rbx-2.7
rbx-2.71828182

2.7.3ないな…

asdf rubyをアプデ

% asdf plugin-update ruby
Updating ruby...
remote: Enumerating objects: 30, done.
remote: Counting objects: 100% (19/19), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 30 (delta 10), reused 14 (delta 10), pack-reused 11
Unpacking objects: 100% (30/30), done.
From https://github.com/asdf-vm/asdf-ruby
   101e86a..ab15a18  master     -> master
   101e86a..ab15a18  master     -> origin/master
Already on 'master'
Your branch is up to date with 'origin/master'.

これでasdfをアプデ出来たっぽい。

確認すると…

% asdf list-all ruby | grep 2.7
1.8.6-p287
2.0.0-p247
2.2.7
2.7.0-dev
2.7.0-preview1
2.7.0-preview2
2.7.0-preview3
2.7.0-rc1
2.7.0-rc2
2.7.0
2.7.1
2.7.2
2.7.3
jruby-9.2.7.0
rbx-2.2.7
rbx-2.7
rbx-2.71828182

来ました。

終わり

asdfは定期的にasdf reshimだったりasdf plugin-updateだったりやってあげたほうがよさげですね。