pnpmをgithub actionsで使う

今更ながらGithub Actionsを触り始めたものの、pnpmを使ってコードを書いておりいきなりハマったのでメモ。

結論

成果物はこれ

いくつかやり方がありそうだが、結論としてはpnpm公式のドキュメント記載の方法で記述するのが良かった。 他の選択肢に比べて、記述が明示的でGithubActionsに慣れていない身としては良かった。 pnpm.io

その他の方法

Githubで検索するといくつか候補が出てくる。 github.com

例えば、pnpmリポジトリで管理されているgithub action

github.com

これは、コマンドの実行方法がよくわからず、スキップ。

他にも個人開発っぽいものがいくつかあるが、よくわからないエラーが出たりしたのでスキップ。

vscode+volarでvue3のref sugerを使う際に型定義のimportでエラーが出る問題の解消

問題

script setup(ref suger)を使っている際、特に何も考えずimportすると、以下のようなエラーが出てしまう。

only refers to a type, but is being used as a value here.ts(2693)

only refers to a type, but is being used as a value here.ts(2693)

解消法

-   import { ControlElement } from '@jsonforms/core';
+  import type { ControlElement } from '@jsonforms/core';

参考 github.com

import typeとすると良いらしい(こんな構文があるのか)

type-only-imports-and-exportというTS 3.8で入った構文らしい。

TypeScript: Documentation - TypeScript 3.8

vue3+vite+storybook+pnpmでハマったことメモ

StorybookがViteに対応していたので、試した際にハマったことなど。

storybook.js.org

pnpm起因の問題は使っている人には参考になるかもしれない。

pnpx sb initが動かない

Storybook for Viteに↓の記述があるので、pnpxとバージョンを読み替えて

npx sb@next init --builder storybook-builder-vite

↓を実行した。

npx sb init --builder storybook-builder-vite

結果動かず、Invalid Packageと怒られてしまう。 (EINVALIDPACKAGENAMEと表示されていた)

実行時 - pnpm : 6.13.0 - npm: 7.20.3 - node: v16.7.0

入れようとしたstorybookのバージョン - @storybook/vue3": 6.3.7

やったこと

↓を叩く

pnpx sb init -s
pnpm install

// storybookの起動に必要
pnpm add -D @storybook/cli
panpm install

-s オプションを付与することで、package.jsonの変更は入るが、パッケージのインストールは実行されない。

pnpx を使った際の動作が内部的にどうなってるのか把握できてないが、pnpm側で実行させることで回避している(?). (内部的にはnpmを使ってたりするのか...?)

参考

github.com

github.com

Storybook 起動時に "Singleton client API not yet initialized" と怒られる

↓のIssueを参考に.storybook/main.jsに変更を加えた。

module.exports = {
+  async viteFinal(config) {
+    // suppress storybook error
+    // https://github.com/storybookjs/storybook/issues/10887#issuecomment-901109891
+    config.resolve.dedupe = ['@storybook/client-api'];
+    return config;
+  },
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  core: {
    builder: 'storybook-builder-vite',
  },
};

github.com

Vue3のプラグインの読み込み方

Vue2までは↓のようにすれば良かったが、Vue3ではcreateAppで作成したオブジェクトに対してプラグインを読み込ませる必要がある。

import Vue from 'vue';

Vue.use(xxxxPlugin);

やったこと

以下のようにする

import { app } from '@storybook/vue3';
import { Button } from 'ant-design-vue';
app.use(Button);

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
};

コンポーネントがstoryでレンダリングされない

consoleを見ると以下のようなメッセージが出ていたので、

      [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue.
      Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".

.storybook/main.jsのconfigにaliasを貼った。

async viteFinal(config) {
    config.resolve.alias = {
      /*
      fix this issue
      [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue.
      Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".
      * */
      vue: 'vue/dist/vue.esm-bundler.js',
      '@': path.resolve(__dirname, '/src'),
    };

Vue3 ,TSのEslintの簡単な設定(@antfu/eslint-config-vueについてメモ)

やること

devDependenciesに以下のパッケージを追加する

yarn add -D eslint @antfu/eslint-config-vue

.eslintrcは以下のようにextendsする

{
  "extends": [
    "@antfu/eslint-config-vue",
  ]
}

以上、楽ちん。

詳細

@antfuというVue本体のコミッターが@antfu/eslint-config-vueというパッケージをメンテナンスしている。

@antfu/eslint-config-vueは以下のように依存関係を作っており、

@antfu/eslint-config-vue
  -> @antfu/eslint-config-ts
    -> @antfu/eslint-config-basic

Vue ,TS,basicの各レイヤーのdependenciesにそれぞれ必要なESLintのプラグインとルールセットが記述されている。

具体的に各パッケージのdependenciesを除くと以下のようになっている。


eslint-config/package.json at master · antfu/eslint-config · GitHub

{
  "name": "@antfu/eslint-config-vue",
...
  "peerDependencies": {
    "eslint": ">=7.4.0"
  },
  "dependencies": {
    "@antfu/eslint-config-ts": "^0.7.0",
    "eslint-plugin-vue": "7.12.1"
  },
  "devDependencies": {
    "eslint": "^7.30.0"
  }

eslint-config/package.json at master · antfu/eslint-config · GitHub

{
  "name": "@antfu/eslint-config-ts",
...
  "peerDependencies": {
    "eslint": ">=7.4.0",
    "typescript": ">=3.9"
  },
  "dependencies": {
    "@antfu/eslint-config-basic": "^0.7.0",
    "@typescript-eslint/eslint-plugin": "^4.28.1",
    "@typescript-eslint/parser": "^4.28.1"
  },
  "devDependencies": {
    "eslint": "^7.30.0"
  }
}

eslint-config/package.json at master · antfu/eslint-config · GitHub

{
  "name": "@antfu/eslint-config-basic",
...
  "peerDependencies": {
    "eslint": ">=7.4.0"
  },
  "dependencies": {
    "eslint-config-standard": "^16.0.3",
    "eslint-plugin-eslint-comments": "^3.2.0",
    "eslint-plugin-html": "^6.1.2",
    "eslint-plugin-import": "^2.23.4",
    "eslint-plugin-jsonc": "^1.4.0",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^5.1.0",
    "eslint-plugin-unicorn": "^34.0.1",
    "eslint-plugin-yml": "^0.9.0",
    "jsonc-eslint-parser": "^1.1.0",
    "yaml-eslint-parser": "^0.3.2"
  },
  "devDependencies": {
    "eslint": "^7.30.0"
  }
}

このように必要なeslintパッケージとルール設定をあらかじめ記述しておくことで、複数のプロジェクトでESLIntの導入で楽ができるようになっている。

結果として、これらをpackage.jsonに追加するだけで良い。

  • eslint
  • @antfu/eslint-config-vue

※ eslintはdependenciesに含まれていないので、プロジェクト側で明示的にインストールする必要がある。

実際にVitesseという、@antfuが作った、ViteベースのVueアプリケーションのスケルトンで使われている。

github.com

感想

それぞれのパッケージはモノレポで管理されており、うまくレイヤーが作れているように見える。
そのため、vueのプロジェクトでは@antfu/eslint-config-vue、reactのプロジェクトでは@antfu/eslint-config-react、TSだけであれば@antfu/eslint-tsを呼べば良い。

必要な分だけESLintの設定をプロジェクトに組み込める仕組みになっていて、こんな管理の仕方があるのかと感心した。

注意点

eslint-config-basicに色々eslintのルールが詰められているので、人によっては気になるところかもしれない。

flutter for web でfile pickerを使う

完成イメージ

import 'dart:convert';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: '',
      theme: ThemeData(
        primarySwatch: Colors.amber,
      ),
      routes: {
        '/': (_) => App(),
      },
      initialRoute: '/',
    );
  }
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(''),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          try {
            final _paths = (await FilePicker.platform.pickFiles(
                    type: FileType.any, allowMultiple: false, withData: true))
                ?.files;
            final data = utf8.decode(_paths.first.bytes);
            print(data);
          } on PlatformException catch (e) {
            print("Unsupported operation" + e.toString());
          } catch (ex) {
            print(ex);
          }
        },
      ),
    );
  }
}

この辺

withData: trueにするのがミソ このフラグを立てることで選択したファイルのbytesにデータが入ってくる。 あとはこのデータがテキストであればよしなにutf8.decodeなりを噛ませればStringとして読み込みできるようになる。

final _paths = (await FilePicker.platform.pickFiles(
        type: FileType.any, allowMultiple: false, withData: true))
    ?.files;
final data = utf8.decode(_paths.first.bytes);

mysql 8.0 PDO error

ことはじめ

mysql:8.0にpdoでつなぎに行ったらこんな感じのエラーが出た。

PDOException: PDO::__construct(): The server requested authentication method unknown to the client [caching_sha2_password] in /var/app/bin/setup.php on line 19

認証方法が8系から変わった?とのことなので、調べる。

調べた

この記事によると、my.confあたりに

[mysqld]
# 略
default-authentication-plugin=mysql_native_password

を追加すると良いとのこと。

解決

mysqlの公式dockerイメージで構築していたので、したのような感じでCMDを渡してあげる。

version: "3.7"

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --default-authentication-plugin=mysql_native_password

補足

  • mysqldの設定をコンテナ起動時にCMDから指定の形式で渡すと変更ができる
  • 渡せるコマンドは今回の場合だとdocker-compose run db --verbose --helpで確認ができる

Configuration without a cnf file Many configuration options can be passed as flags to mysqld. This will give you the flexibility to customize the container without needing a cnf file. For example, if you want to change the default encoding and collation for all tables to use UTF-8 (utf8mb4) just run the following:

 $ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

If you would like to see a complete list of available options, just run:

$ docker run -it --rm mysql:tag --verbose --help

ref

alpine composer

コンポーザーをalpineに楽に入れたい。 (※alpine以外の環境にこの方法で入れて動くかは不明)

結論としては、こんな感じにすればOK

FROM alpine:3.10.3

ENV COMPOSER_HOME /composer
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV PATH /composer/vendor/bin:$PATH

COPY --from=composer:1.9.1 /usr/bin/composer /usr/bin/composer

解説

composerは公式イメージがあるので、ここから実行ファイルだけ落としてくれば動くみたい。
/usr/binとかに入れておく。

COMPOSER_ALLOW_SUPERUSER 1

これを指定すると、composerをSUPER USERで動かしても怒らなくなる。
alpineはそのままだとSUPER USERで動くので指定している。
実行ユーザーを設定していたりすれば不要なんだろうと思う。

COMPOSER_HOME

指定したディレクトリにcomposerそのものが動作するのに使用するファイル、キャッシュ類が格納される。

応用

ユースケースとして、デプロイ用のイメージでcomposer installをしたいが、アプリ動作時はcomposerが不要になる場合は下記のようにするとイメージが綺麗に保てるのでは(と思っている)。

FROM php:latest

COPY --from=composer:1.9.1 /usr/bin/composer /usr/bin/composer

WORKDIR /var/some-application
COPY ./some-application .

RUN composer install --no-dev \
 && composer some-application-set-up-script \
 && rm -rf /usr/bin/composer /composer