UoPeople(Bachelor, CS)に入学してみる(続)

この記事の続き

shotanue.hatenablog.com

結論

ほぼ入学できた。

英語テスト

なんやかんやテストが受けられて、点数は100点(=英語の勉強クラスに突っ込まれる足切りをギリギリ超える点数)だった。

ちなみにDuolingoTestはリーディング・ライティング・リスニング・スピーキングの4種全てやるタイプで、日本人らしくライティング・スピーキングが絶望的にダメだった。

英語はパスできたので、あとはオンライン大学のお作法だったり、アメリカの大学がどんなものかを学ぶオリエンテーションをパスすると晴れて本入学となる様子。テストがあるようだけど、オリエンなんできっと大丈夫でしょう...。

卒業証明書関連

卒業証明書と身分証明書(パスポート)の名字が不一致問題

結婚して名字が変わっていたので、卒業証明書と身分証明書(パスポート)の名字が不一致問題が起きそうになった。

まず、もし卒業証明書が旧姓の場合にそれが自分の旧姓である証明をする必要があり、証明するには簡単に調べて2種類方法があるがそれぞれデメリットもある。

旧姓併記パスポートを用意して証明する

  • 旧姓併記パスポートで他国に入国しようとすると止められがち(らしい)
  • パスポートのICチップに旧姓情報は載せられず、表記とICチップ、加えて飛行機のチケットと名前が一致しなくなりがりで入国時に止められることがあるとのこと。

戸籍謄本を翻訳して、翻訳文に公証をつけて証明する

  • 戸籍謄本の英訳文は基本的には自治体で発行できない
  • 翻訳会社などに依頼して、その翻訳が正しいと公的な機関にお墨付きをもらう必要がある(らしい)
  • 一部自治体は英訳も発行できるようだが、各自治体があくまで自主的に行なっているサービスな様子
  • 単純にお金と時間がかかる

「改姓はご自身で提出先に証明してください」

この面倒くささがある中、いざ大学の申請ページを見ると「改姓はご自身で提出先に証明してください」と書いてある。詰んだ。

と思ったが、卒業した大学側に念の為聞いてみたところ、英文で発行する卒業証明書については現在の名字で発行可能ということだったので難を逃れた。本当に旧姓のみだったらどうしようかと思った。

これから

先人のブログ

empitsu88.hatenablog.com

を読んでいると数学は結構詰め込まれそうなので、文系😇だった身としてはその辺の勉強はやり直しておいた方が良いかなという状況ではある。数学ちょっと楽しみではあるんだけど。

入学手続きの完了したタイミング的に多分4月くらいから本格的に始まりそうなので、それまでに一定なんとかしておきたいなあ。

UoPeople(Bachelor, CS)に入学してみる

2023年の抱負にUoPeople入学したいと書いたので、勢いで入学手続きを進めてしまった。ちゃんと$60支払った。

後述だが英語テストの受験で支払い周りの関係で詰まってしまったのでまだ入学手続き中ではあるが、1歩踏み出した感はある。

ひとまず学士のCSコースに申し込んだので、本入学まで進められればいざ社会人学生の幕開けになる。

英語テストを受けるまでにやったこと、事前に見聞きしていた情報と違ったところなどメモしておく。

Application

UoPeopleのHP上にApplyボタンがあるのでクリック、個人情報入れてあれこれすると$60の決済を求められたので、決済するとアカウントが作成されて学生用ポータルへログインできるようになる。

決済方法はGooglePayが選べたもののエラーになるので、PayPalで決済した。クレジットカード直入力も可能そうだったが、PayPal使えるなら直入力は避けたかったのでPayPalにした。

Portal

ポータルへログインすると追加の個人情報の入力が求められる。追加で必要な情報として最終学歴と住所を求められた。

色々と入力を求められるものの、ポータル自体が重い(ローディングが長い)ので時間に余裕が必要だった。

最終学歴について

色々と先人のブログを見ていると高校の証明書を出したり、実際UoPeopleのHPには高校卒業の証明が求められる記述があった。てっきり高校に英語記述の卒業証書を依頼する羽目になるのかなと思っていたが、ポータルの入力欄ではAn associate/bachelar/master's degreeとあった。ということは大学の卒業証書でも良さそうなのでこれは楽かもしれない。

英語テスト

本入学にあたって英語力を証明しないといけないが、あいにくTOEFLの点数(良いもの)を持っていない。フォームを進めていくとどうやらDuolingo Testというのがあるらしく、証明できない人はこれを受験すれば良いらしい。FoundationClassの英語レベルがMaxになるとのことなので、一応受けておくことにした。

なお練習テストの結果は↓だった。

ポータル上でDuolingo Testの支払いを完了して、いざDuolingo側でテストを受けようとするとテスト購入を案内されてしまう問題にぶち当たってしまった。。。 PaymentHistory的にはPaidになっているので謎である。

ちょっとよくわからないので、Advisorに問い合わせをして回答待ちになってしまったので一旦ストップに。

2022の振り返りと2023年の抱負

振り返りと抱負を考えたいなと思ってたら年越してしまったので書く

2022

仕事

エンジニア業を続けられており、コードも書いている。

プロジェクトのPHPバージョンを8.2対応させたり、E2E(Playwright)入れたり、GitLab CI書きまくったり、リファクタリングばかりやっていた。 バージョンアップ対応ではrectorなんかを試してみたり、Playwrightなり年間を振り返ると要素技術的には色々学びはあったものの、機能開発してユーザーに価値を届けるようなお仕事は21年より少なくなってしまった。

長いことフロントエンドやりたいやりたい言っていたら、ありがたいことに新たにNext.jsを触る機会に恵まれたので、2023年はその方面で頑張れそうでモチベは上がりそう。

あと、今の会社には入社して3年半になった。新卒3年目で4社目の身としては、継続して務められていてとても偉いと思う。(前の会社は倒産しちゃったりでタイミングが悪かったなどあるが)

プライベート

都内を離れ、チョット遠くに引っ越した。大通り沿いのマンションに住んでいたが、空気が部屋の換気口がススだらけになるくらい汚く、ダイソンの空気清浄機がいつもアラートを出していたが、引っ越してからは常にグリーンでautoモードだと全く仕事をしない子になってしまった。喘息の関係で季節の変わり目にゼーゼーしがちだった問題も、引っ越してからは一度もない。もし向こう数年の様子で落ち着くようだったら空気の違いなんだろうと思う。

家電

今年も色々買った。

HITACHIのドラム式洗濯機

Bic Cameraで安くなってたので購入。

諸般の事情により在宅エンジニアは家事をこなさないといけなくなりがちなので、洗濯周りの家事が劇的に楽になった。基本的に乾燥までいつもやらせている。

ドラム式洗濯機だったので購入時に気をつけるべきポイントがわからなかったが、乾燥をフル活用するなら「埃フィルターの掃除しやすさ」が大事な気がした。購入したモデルのフィルターは4つのパーツから構成されており、乾燥を数回行うたびにそれぞれ分解して掃除機で埃を吸っているが、とにかく溝が多く埃が溜まりがち。こういう部分はいかに溝を減らすか、パーツを減らせるかが勝負な気がするが、可動する取っ手を付けてみたりギミックを作ったがために溝が増えていてデザインが悪い気がした。作る側としては色々事情があるのかもしれないが。

roomba i7+

ブラックフライデーで半額になっていたので購入。初ルンバ。

諸般の事情により在宅エンジニアは家事をこなさないといけなくなりがちなので、日々の掃除機タスクが減りとても助かる。

n1なので他社製品との比較ができないが、結構賢い印象を受けた。例えばダイニングテーブルの近くで動かしていると「これダイニングテーブルじゃね?違う?」とアプリで聞かれたりするし、間取りを学習しつつ自動でリビングなり廊下なりラベル付もするので、iRobot側で家データ沢山持ってて学習させてるんだろうか。この辺はやはりシェア取った会社の強みなのかなと思ったり。

よく頑張ってくれているので、今後はブラーバと2台連携も視野に入れたい。(物欲)

不満は特にないが、掃除終わって母艦へのゴミ吸引がバカうるさいなとは思った。

PlayStation 5

Apex中毒者になってしまった。ゲーム自体全然やっておらずなんならFPSGCメダルオブオナーぶりなので、15,6年ぶりになりそう。

とりあえずシーズン12・Split1から初めて半年くらいでプラチナ3まで上がった。

ダイエット

2022年1月に比べると5-6kg体重が落ちた。引っ越したことで近所からジャンクフードが減り、外食が減った影響が大きい。

AnytimeFitnessの契約をしてみたものの、1ヶ月で続かなくなってしまった。仕事で忙しく無理だった。

自動車免許

2021の夏終わりくらいから、2022年3月にかけて、28にして初めて取った。引っ越し先が車社会の地域なのでどうしようもなく取った。

社会人が仕事と並行して自動車免許を取った形だが、川崎の教習所の教習カレンダーは全然空いておらず、フルリモート+裁量労働がなければスケジュールの捻出が大分困難だった。福利厚生なかったら教習受け切ることがまず難しかったので会社とチームにはとても感謝している。

1−3月は休日が全て免許の勉強なり教習で潰れたのでプライベートは死んだ。

2023

仕事

4月でエンジニア7年目になる。今のチームでは色々あったおかげで、コミュニケーションで色々調整図る能力は多少身についた感がある。とはいえエンジニアとしてはやはり技術面で頭を捻りたいので、お仕事としてはやはりその方面、特にフロントエンド周りに割合多めで今年は頑張りたいなとは思う。

フロントエンドモノレポ(pnpm workspaceなど)、Playwrightだったりを去年仕事に突っ込んで一定手応えがありつつ、別プロダクトを触るようにもなったので布教していけたらと考えている。既存のNextjsプロジェクトをどうやってスケールさせていくかが大きなテーマになりそう。

University Of The People, Computer Science

UoPeopleというらしい。要は大学生になってCSの勉強をしたい。

学位取ったりしたところで給料に直結するものでもないと思うが、向こう三十数年コンピューターで食っていくならちゃんと勉強しといて損はないかなと思っている。

海外のオンライン大学のコンピューターサイエンス学部なので、英語と数学がボトルネックになりそうだが、特に文系民としては数学どうしようという気持ちではある。このタイミングで苦手意識を払拭したいなとも思うが。

ネットを見ると先人が沢山いそうなので、その辺の界隈でコミュニケーションとってみるのも良いかもしれない。皆頑張っていそうだが不思議と「UoPeopleのCS卒業したよ!」という記事が見当たらないのは不安ではある。見つけられてないだけだと祈る。

Apex

ダイヤ帯に上がりたい。社会人でも目指せるラインだとは思う。

料理

妻が激務すぎて自分が弁当を作るようになったので、この流れでゆっくり色々覚えたい。家を建てており、キッチンのグレードは盛っていて使いやすいはずなので、これはいい機会な気がする。

まとめ

2021年までは忙しさが「仕事>>>>プライベート」だったが、2022年は「仕事=プライベート」くらいになっており、なんとなく2023年は「仕事<プライベート」になりそうな予感がする。UoPeopleに入ろうとしているあたり自分で鞭を打っている感があるが、業務量ぶっ壊れなければなんとかなるだろとも思う。

こういうなんとかなるだろという見積もりの甘さによって業務量がぶっ壊れたりして、一昨年くらいは訳わからない働き方をしてしまってたのだが、その辺のコントロールを改めて身につけるのも、ある種今年の大きなテーマなのかもしれない。

2年ぶりn回目のブログ?作り

shotanue.devを作った

はてなブログ」や「Qiita」から記事を集約してインデックスを作るポータルみたいなページを作った。

2年前に一時期Gatsbyでshotanue.devドメインで技術ブログを運用していたが、Gatsbyのアップデートに失敗してめんどくさくなり捨ててしまっていた。が、後述のモチベにより新しく作り直した。

書いた記事を集約するサイトを作ったモチベ

技術記事を投稿するサイトは色々あって、上述以外にも「Zenn」「medium」「note」があったりするが、記事が書いた時に散らばっていく感じが気になっていた。全然記事書いてないけど。

このあたりの解決策は「LAPRASポートフォリオ」などあると思うが、仕事でNext.jsを触る機会があって素振りしたく、いい感じのお題を探していたのもあって自前で構築した。

(あと、この類のサイトで先人がおり、良いアイデアだなあと思っていたというのもある。)

www.komtaki.com

panda-program.com

つかったもの

以下の観点であれこれ考えた結果、このチョイスをしてみた。

  • 簡単に作りたい
  • AWSなりでサーバー運用はしたくない
  • ガワはRemixなりAstroで何か作りたい発作が出た時に捨てやすくしておく

リソースサーバーをGASで作る

はてなブログ」や「Qiita」から記事を集約する処理を書いた。Claspを使うとTypeScriptで書けるが、FetchしてレスポンスのXMLなりJSONを成形するだけで大した処理でもなく、簡単に作りたかったので使わなかった。実装は以下で済んでいる。

function doGet() {
  const posts = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('posts').getDataRange().getValues();

  return ContentService.createTextOutput(JSON.stringify({
    posts: posts.map(x => ({
      kind: x[0],
      id: x[1],
      title: x[2],
      link: x[3],
      publishedAt: x[4],
      updatedAt: x[5],
    }))
  }));
}


function main() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('posts')

  const posts = [
    ...hatena(),
    ...qiita(),
  ];

  sheet.clearContents();
  const range = sheet.getRange(1, 1, posts.length, 6);
  range.setValues(posts);
  range.sort({column: 5, ascending: false});
}

function fetchEntriesFromAtom(url, kind) {
  const xml = XmlService.parse(UrlFetchApp.fetch(url).getContentText());

  const atomNs = XmlService.getNamespace('http://www.w3.org/2005/Atom');

  return xml.getRootElement().getChildren('entry', atomNs).map((entry) => {
    const pick = (node) => entry.getChild(node, atomNs).getValue();
    const pickAttribute = (node, attribute) => entry.getChild(node, atomNs).getAttribute(attribute).getValue()

    return [
      kind,
      pick('id'),
      pick('title'),
      pickAttribute('link', 'href'),
      pick('published'),
      pick('updated'),
    ];
  });
}

function hatena() {
  // feedからは100件までしか取得できずページングが無いのでちゃんとやるなら別APIを使わないといけないが、記事がそもそも100件に全然達してないのでこれでヨシとした。
  return fetchEntriesFromAtom('https://shotanue.hatenablog.com/feed?exclude_body=1&size=100', 'shotanue.hatenablog.com');
}

function qiita() {
  const entries = UrlFetchApp.fetch('https://qiita.com/api/v2/users/shotanue/items?page=1&per_page=100').getContentText();
  return JSON.parse(entries).map((entry) => {
    return [
      'qiita.com',
      entry['id'],
      entry['title'],
      entry['url'],
      entry['created_at'],
      entry['updated_at'],
    ];
  });
}

接続するサービスが増えても、足していく場所は決まっているのでまあ大丈夫でしょう(多分)

  const posts = [
    ...hatena(),
    ...qiita(),
    // ここに足すだけ
  ];

このスクリプトのmain関数を実行すると以下のようなシートが出来上がる。

で、doGetを実装してあるので、GASをWebAppでデプロイしてAPIを立ててあげて、リソースサーバーの出来上がりということになる。

ガワをNext.jsで作る

Next.jsのGetting Started読みながら、

pnpm create next-app

を叩いて作った。

特に難しいことはしておらず、なんならhttps://github.com/igoradamenko/awsm.cssというClass less cssを使ってスタイル周りで手を抜いたおかげで、マークアップが大分単純になった。あまり中身がないというのもあるが。

<main>
    <section>
        <h2>Shotaro Hirukawa(@shotanue)</h2>
        <p>Hello there, I am a web developer. Working in Japan.</p>

        <p>Find me on</p>
        <ul>
            <li><a href="https://github.com/shotanue" target="_blank" rel="noreferrer">GitHub</a></li>
            <li><a href="https://twitter.com/shotanue" target="_blank" rel="noreferrer">Twitter</a></li>
            <li><a href="https://shotanue.hatenablog.com/" target="_blank" rel="noreferrer">Hatena Blog</a></li>
            <li><a href="https://qiita.com/shotanue" target="_blank" rel="noreferrer">Qiita</a></li>
        </ul>
    </section>
    <section>
        <h2>articles</h2>
        {posts.map((post) => (
            <article key={post.id}>
                <h3><a href={post.link} target="_blank" rel="noreferrer">{post.title}</a></h3>
                <aside>
                    <time
                        dateTime={format(new Date(post.publishedAt), 'yyyy-MM-dd', {timeZone: 'Asia/Tokyo'})}
                    >
                        {format(new Date(post.publishedAt), 'yyyy-MM-dd', {timeZone: 'Asia/Tokyo'})}
                    </time>
                    <div>
                        <span>{post.kind}</span>
                    </div>
                </aside>
            </article>
        ))}
    </section>
</main>

デプロイ

本当はCloudFlare Pagesを使いたかったが、慣れておらず、pnpm起因の問題などでビルドが全然上手くいかずyak shavingになってしまったのでVercelを使った。CloudFlareはまた今度時間がある時に見たいと思う。

というわけでしばらくVercel運用になる。

感想と今後

初めてGASでAPIを立てたが思ったより簡単にできたので、みんなこのへん触りながらMVP作ってるのかなと思いを馳せた。オレオレツール作るのにも便利そう。

今回のページ自体は勢いで作ったので「見た目もうちょっとどうにかしたい」「フッターがない」「GA入ってない」とか色々やることはあるが、Markdown for the component era | MDXが技術的に結構面白そうなので、shotanue.dev自体で記事をホストする方向でこの辺突っ込めないか色々企んでいる。

RaspberryPi zero2 Wにhomebridgeを入れて起動するまで

RaspberryPi zero2 Wにhomebridgeを入れて起動するまで

ことはじめ

スマートホーム沼に興味を持ち始めているのですが、homebridgeを使うと色々遊べるらしいというのを知りました。homebridgeはnodeのツールなのですが、raspberry piなどのマイコンで動かすのがよくあるケースとのことです。たまたまRaspberry pi zero2 wを購入できたので、初期セットアップからhomebridge起動までの記録を残しておこうという次第です。

購入したもの

Raspberry pi zero2 w スターターキット

本体のみ購入が売り切れている中、若干お値段が張りますがこちらは残っていたので購入できました。

前提

MicroSDへのOS書き込み」といったセットアップにはMacを利用します。

目指すところ(動くまでにやったこと概要)

  • Rasbian OS liteをMicroSDに書き込む
    • Raspberry Pi Imagerを利用します
    • wifi, sshの情報もOS書き込み時に一緒に含められます
  • Raspberry Pi上にdocker-composeをインストール
    • aptで入ります(ちょっと古いバージョンっぽい感じがしますが)
    • dockerも一緒に入ります
  • homebridgeの起動
    • docker-composeを使います

Raspbian OS lite(64-bit)をMicro SDに書き込む

スターターキット付属しているMicroSDカードにはすでにRaspbian OSが書き込まれているのですが、Desktop環境は不要なのでlite版をインストールします。

インストールにはRaspberry Pi Imager v1.7.2を利用しました。

wifi, sshの設定をする

右下の歯車をクリックするとwifisshのセットアップまで可能になっています。

入力フォームに従ってぽちぽち入力すれば良いです。

Raspberry Pissh接続する

電源を入れます。しばらく待つとsshで接続できるようになります。

# 公開鍵なりのセットアップはしてあるものとします
# ユーザー名とホスト名が初期設定のままであれば、↓でログインできます。
ssh pi@raspberrypi.local

docker、docker-composeをインストール

↓を叩きます。docker-composeをインストールするとdockerも一緒に入ってくれます。

sudo apt update

# vimはdocker-compose.ymlの記述で使いました。必要なければ不要です。
sudo apt install docker-compose vim

実行後↓を叩いて動けば大丈夫です。

sudo docker ps

homebridgeを起動する

docker-compose.ymlをどこか任意のディレクトリに置きます。

restart: alwaysをセットしているのでRaspberry Pi起動時に自動で立ち上がるようにしました。

version: '3'
services:
  homebridge:
    image: oznu/homebridge:ubuntu
    container_name: homebridge
    restart: always
    network_mode: host
    environment:
      - HOMEBRIDGE_CONFIG_UI=1
      - HOMEBRIDGE_CONFIG_UI_PORT=8080
    volumes:
      - ./homebridge:/homebridge

↓で起動します。

sudo docker-compose up -d

無事起動できていれば、http://raspberrypi.local:8080/にアクセスするとhomebridgeの初期設定画面が開きます。

JSONForms AntDesign Vueを書いている

JSON Formsって何よ

JSON Schemaを食べさせるとフォームを作ってくれる便利なライブラリ。

jsonforms.io

アーキテクチャのページを見るとわかるが、

  • core
  • binding
  • renderer

の3層構造になっている。

そのためReactやVueにべったりした作りにはなっておらず、JSONForms側で用意がないUIフレームワークの場合でも自前でrendererを書くことでJSONFormsが利用可能になる。

そこで、今回Ant Design VueというVue向けのUIフレームワークJSON Formsに対応させるrendererを作っている。

このフレームワークはプロダクトでも使っていて使いやすく、個人開発にでも使うかと思い、折角なので作ってみようと思った次第となっている。

※ ちなみにAnt Designはアリババが作っているUIフレームワークreact向けの実装もある

作れるフォームも結構柔軟で、json schemaのoneofなどにも対応していることでスキーマ定義をするだけでタブ切り替えだったり、動的にinputのリストを増減なんかもできる。そのため、ある程度複雑なフォームにも対応が可能になっている。

この辺はexamplesを見ると結構面白い。

https://jsonforms.io/examples/combinators

Dockerのイメージビルド時に環境変数が未定義の場合にエラーを出したい

まとめ

Dockerfile側ではエラーを出すようなENVの定義方法がなさそうで、docker image buildを叩いている場合は厳しそうに見える。

ワークアラウンドではあるものの、イメージビルドにdocker-composeを利用している場合、envプロパティを変数展開する際にエラーにできる。

docker-compose.yml の定義方法

services:
    some-service:
        image: xxxxx
        environment:
          - FOO=${FOO:?}    

とする。

variable substitutionの箇所に記載がある。 Compose file version 3 reference | Docker Documentation

Similarly, the following syntax allows you to specify mandatory variables:

${VARIABLE:?err} exits with an error message containing err if VARIABLE is unset or empty in the environment. ${VARIABLE?err} exits with an error message containing err if VARIABLE is unset in the environment.

Dockerfileではできないの

以下はFOOが未定義でもビルドが通ってしまう。

FROM xxxxx

ENV FOO=${FOO:?}

参考

kdnakt.hatenablog.com

pnpmをgithub actionsで使う

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

結論

2021/12/24追記 いまは公式のactionsではなく、nodejsをセットアップするactionsを呼びつつ corepackを使って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+TypeScriptプロジェクトでEslintをお手軽に設定するメモ(@antfu/eslint-config-vueについて)

tldr

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

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

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

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

以上、楽ちん。

詳細

@antfuというVue本体のコミッターが@antfu/eslint-config-vueというパッケージをメンテナンスしている。 eslintのプリセットだが、設定をモノレポで管理しつつ、レイヤーになっていて、賢い管理の仕方だな思ったのでメモ。

モノレポの構造

モノレポでは↓のパッケージが管理されている。

  • @antfu/eslint-config-basic(eslintの共通設定)
  • @antfu/eslint-config-ts(ts用の設定)
  • @antfu/eslint-config-vue(vue用の設定)
  • @antfu/eslint-config-react(react用の設定)
  • @antfu/eslint-config(全部入り)

これらのパッケージは, それぞれ依存関係があり、以下のようになっている。

@antfu/eslint-config-ts

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

@antfu/eslint-config-vue

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

@antfu/eslint-config-react

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

@antfu/eslint-config

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

@antfu/eslint-config-basicは共通設定として、他のすべてのパッケージから読み込まれている。
@antfu/eslint-config-tsはbasicを継承しつつ、TypeScript用のeslint設定が加えられている。
@antfu/eslint-config-vueはbasic, tsを継承しつつ、Vue用のeslint設定が加えられている。

...というように、@antfu/eslint-config-react, @antfu/eslint-configも同様に継承関係が作られている。

メリット

継承にはなるが、パッケージの組み合わせでeslintの設定管理ができる。

なので、例えばここに「svelteのプロジェクトを増やしたい」となったら、basicとts用の設定を継承したsvelte用パッケージを追加するだけで済む。

複数プロジェクトでESLIntの設定を共通化したいときに、レイヤーかは管理方法のひとつのアイデアとして良いかもしれない。

使い方

Vueのプロジェクトであれば、以下のパッケージを追加し、eslintrcなどでextendsに@antfu/eslint-config-vueを指定すれば良い。

  • eslint
  • @antfu/eslint-config-vue

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

余談

@antfu/eslint-config-vueについては、実際にVitesseという、ViteベースのVueアプリケーションのスケルトンで使われている。(Vitesseも@antfuが作ったものという) github.com

注意点

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