絞り込み

最新の投稿

プログラミング Polygon
プログラミング

【2022年度新入生向け】生協でWindows11のパソコンを買ったらやること

俺は今体調が悪くてこんな記事書いてる場合じゃないんですが、とんでもない事実に気づいたので急遽書きます。 https://www.microsoft.com/ja-jp/d/surface-pro-8/8qwcrtq8v8xg 生協がSurface Pro 8に切り替えた場合、Windows10を知らない世代が入学する可能性があるんですよ。 今販売している「Pro7」なら10ですけど、国内メーカーが既に「11プリインストール」機種を売ってるので、「最初から11」の学生が来てもおかしくない。 そこで、 「10を知らない世代が電算研に来た場合、最低限やっておいてほしいことのチュートリアル」を書きました。 1. ユーザーフォルダがメアドになってる?マイクロソフト(以下MS)は近年、「MSアカウントの~~強制的な~~OS紐づけ」を強行してきました。 そしてWindows11 Homeではついに、ライセンス認証の段階で機内モードをONにしない限り、強制的にサインインを要求されるというとんでもない仕様に変更されました。 (SurfaceはHomeライセンスですが、自作するならProのライセンスを買いましょう。) その結果、 「MS垢のメアドの@より前」が、そのまんまフォルダ名になるという仕様になっているそうです。俺はローカルアカウントで作ったので検証できてません。 これにより、「事故でフォルダ名を日本語にしてしまう」可能性はなくなりました。 しかしですね、 フォルダ名になる前提で、メアド決めましたか? メアドなんて「名前+誕生日」または「推しの名前+推しの誕生日」にしかしないと思うんですが。てか、岡大Gmailなら「ランダム」じゃん。理不尽すぎる。 https://pc-karuma.net/change-windows-11-user-folder-name/ ということで、恥ずかしいなと思ったらこちらの記事を参考に変えましょう。 2. 拡張子を表示 なぜ最初から表示しないのか? は置いといて、これだけはマジで表示しといてください。 なんだこれ太古のOffice2003か? リボンを廃止した結果がこれだよ!なんだこの多重階層メニューは。 3. タスクバー左に寄せて! これはもう人間関係の問題です。 先輩にパソコンを渡す場面を想像してみてください。 ここに隙間があると、この変化に適応するために余分なエネルギーを使うんですよ。 タスクバーを左クリック→「タスクバーの設定」→「タスクバーの動作」から、「左揃え」に変えてください。 4. カッコいいターミナルを最速で開く方法を覚えよう 単にターミナルって書くと語弊があるんですが、「Windows Terminal」が正式名称だと思います。「コマンドプロンプト」だのなんだのをタブでまとめてくれるやつです。 https://www.microsoft.com/ja-jp/p/windows-terminal/9n0dx20hk701 Windows10の時は自分で入れる必要があったんですけど、11なら同梱されてます。 管理者権限付きでターミナルを開く方法は、Win+X→A の順番でキーを押すだけです。めっちゃ使うので覚えときましょう。 5. wingetでChromeを入れよう winget を使えば、Edgeの嫌がらせスパム通知なしでGoogle Chromeを入れられます。 winget search chrome これで検索したらGoogle.Chrome があるはずです。 winget install Google.Chrome わーい! 6.GitHubアカウント作ろう もはや「最低限やっておいてほしい」というか「開発環境」の記事になってますね。 いつかたっぷり使ってもらう😈ので、作っといてください。 7. VSCode入れよう 「VSCode」を入れてください。 https://code.visualstudio.com/ 難しいこと考えずに入れちゃってください! 詳しくは説明しませんが、最低限やることがあります。 「設定の同期」で、「GitHubアカウント」を選んでサインインしましょう。設定や拡張機能が同期できます。 あと、こちらの拡張機能パックを入れておいてください。後述のWSLと連携できるようになります。 8. WSLを入れよう 情電数理系なら必須ですね。(これを書いている人は機械システム系です。←は?) 「WSLとは何ぞや」など、ここでは説明しないので、詳しい人に聞いてください。要するに現在の部長です!!! Win11なら楽になった Windows 10の場合、「バージョン古すぎてWSL使えん」→「逃げ続けていたWindows Updateを仕方なく受け入れる」→くっそ時間かかるという悪夢が発生するんですが、11は考えなくていいです。 仮想機能の有効化 Get-WindowsOptionalFeature -Online -FeatureName *plat* この結果、VirtualMachinePlatformがDisabledなら以下の手順を行ってください。 Enable-WindowsOptionalFeature -online -featurename VirtualMachinePlatform WSLのインストール wsl --install これだけでUbuntuが使えるようになります。(Ubuntu嫌いな部員はいない前提で書いている) カーネルのアップデート https://docs.microsoft.com/en-us/windows/wsl/install-manual#step-4---download-the-linux-kernel-update-package 俺の環境だとこれが必要でした。 ...以上です。詳しいことは部長に聞いてください。 参考 https://kb.seeck.jp/archives/16953

> 内容を見る

プログラミング Polygon
プログラミング

コンピュータ弱者、初めてのシェルスクリプト

恵まれたソフト(illustrator)で初めて作ったカスみたいなサムネそうだ、シェルスクリプト作ろう。きっかけはBlenderのアドオン開発環境を整えようとしたことであった。ただ普段の制作環境とアドオン開発環境を分けたかっただけなのだ。 しかし自分は数行のバッチファイルを書く数分をケチるために最終的に数時間を費やしてしまったのだ。Blenderの設定はどこにある?まず、基本的にWindowsにおいてBlenderの設定ファイルは初回起動時にC:\Users\(ユーザー名)\AppData\Roaming\Blender Foundation\Blender\(バージョン名)に作られる。 詳しい説明は省くが、そこからconfigdatafilescriptsへと派生する。そして次回起動時からそのフォルダにあるファイルの設定を元に新規ファイルが作られる。 今回大事なのはconfigとscriptsである。このフォルダにスタートアップファイル、プリファレンスファイル、プリファレンスでインストールしたアドオン等が含まれているのだ。 単にデスクトップにあるBlenderアイコンをダブルクリックするだけでは、この一つの設定しか使えない。つまり制作環境とアドオン開発環境を分けることができない。 何か方法がないか探してたところ、「環境設定を複数用意する方法・PC/バージョン間で設定を共有する方法【Blender】」という記事を見つけ、方法3.別設定で開く.batファイルを作るを参考にしてバッチファイルを作った。これで一件落着のはずであった。 だが、自分はこう思った。「新しい環境を作るのにいちいちフォルダを作ってちまちまとパスをコピーするのも面倒だし、プログラムを書いてコンピューターに作らせればいいじゃん」 そのせいで苦しむ羽目になったのである。とりあえず書いてみる恥ずかしながら今まで自分はコマンドプロンプトしか使ってなかったので、Windows PowerShellをちゃんと勉強したいという思いがあった(PowerShellのほうがいろいろ出来そうだし)。 そして以下のようなメモをとった。(実際は色々調べながら書いてるので、最初からガチガチに固めて書いたわけじゃない) これを元にPowerShellスクリプトを書いていく。ちなみに自分はVisual Studio Codeを使っている。 まず、スクリプトが引数を受け取るためにはParam() という関数を使えばいいらしいので、Param($name_pref, $name_bat, $version_blender)と書き、blender_add_env.ps1 として保存した。しかしながら、ここで「変数の型どうなってんねん」と感じたので、` $name_pref.Gettype() $name_bat.Gettype() $version_blender.Gettype()と追記し、ターミナルで.\blender_add_env hoge hogehoge 3.0 と実行すると、IsPublic IsSerial Name                                     BaseType -------- -------- ----                                     -------- True     True     String                                   System.Object True     True     String                                   System.Object True     True     Double                                   System.ValueTypeと表示される。$version_belnderがDouble型になってるのがどうも嫌だ。2.93などの中途半端な数字ならまだしも、Blenderのバージョンはもうすぐ3.0になる。このままだと3.0の0の部分は無視されてしまう。それに事故を防ぐために引数は全部String型で受け取りたい。なのでParam(     [string]$name_pref,     [string]$name_bat,     [string]$version_blender )と書き直した。ついでに(個人的に)見やすくした。受け取ったフォルダの名前で現在の階層に新たなフォルダを作る新しいフォルダに移動するconfigとscriptsのフォルダを作るは簡単なので、さっさと調べてNew-Item $name_pref -ItemType Directory Set-Location $name_pref New-Item config, scripts -ItemType Directoryと最初は書いた。しかし「いちいち移動するの無駄じゃん、一行でいけるやろ」と感じたので、New-Item $name_pref/config, $name_pref/scripts -ItemType Directoryと書き直し、さっきのコマンドを再び実行すると、 このように新しいフォルダーがきちんと得られたのでOKである。 次はconfigフォルダとscriptsフォルダの絶対パスを相対パスから得たい。これをするためにはConvert-Pathを使えばいいらしく、$path_config = Convert-Path "$($name_pref)/config" $path_scripts = Convert-Path "$($name_pref)/scripts" $path_config $path_scriptsと書いた。後半の2行は確認用である。例によってあのコマンドを実行すると、 となり、ちゃんと絶対パスが得られることがわかった。 とりあえず空のバッチファイルをNew-Item で作る。$bat_file = New-Item "$($name_bat).bat"そしてAdd-Contentでバッチファイルに追記していく。Add-Content $($bat_file) "set BLENDER_USER_CONFIG=$($path_config)" Add-Content $($bat_file) "set BLENDER_USER_SCRIPTS=$($path_scripts)" Add-Content $($bat_file) "cd C:\Program Files\Blender Foundation\Blender $($version_blender)" Add-Content $($bat_file) "blender.exe"これで終了である。(書くのが面倒くさくて後半投げやりになってしまった)完成だと思った?blender_add_env.ps1だと長ったらしいし文法的におかしい気がするのでaddbleenv.ps1に改名した。 中身はこんな感じである。Param(     [string]$name_pref,     [string]$name_bat,     [string]$version_blender ) New-Item $name_pref/config, $name_pref/scripts -ItemType Directory $path_config = Convert-Path "$($name_pref)/config" $path_scripts = Convert-Path "$($name_pref)/scripts" $bat_file = New-Item "$($name_bat).bat" Add-Content $($bat_file) "set BLENDER_USER_CONFIG=$($path_config)" Add-Content $($bat_file) "set BLENDER_USER_SCRIPTS=$($path_scripts)" Add-Content $($bat_file) "cd C:\Program Files\Blender Foundation\Blender $($version_blender)" Add-Content $($bat_file) "blender.exe"これでやっと終わりだと思った。現実はそうではなかった。 結論から言うと、さらにこのaddbleenv.ps1を実行するバッチファイルを作らなければならなかった。 というわけで、powershell C:\z_ps_script\addbleenv.ps1 %1 %2 %3という内容のaddbleenv.batを書いた。(説明すっとばしますごめんなさい) これをSystem32にぶち込み、エクスプローラーでaddbleenv hoge hogehoge 2.93と実行してみると ! !! !!! !!!! !!!!! で、出来てる~! ~完~ あとがきはじめてじぶんのためにプログラムをかけたのでうれしかったプログラムのかきかたぐだぐだてすともぐだぐだぶんしょうもぐだぐだよくかんがえたらがぞうむだなのある こーどぶろっくでかけやすいこうだるいこんなくだらないプログラムをかくのにふつかもかかったきちょうなさくひんせいさくじいかんをむだにしたけっかんはしらんおまけ

> 内容を見る

プログラミング Polygon
プログラミング

競プロ環境構築#3 Python編

Windowsの方はWSLを入れて読んでください。 Pythonはいいぞ。Pythonはクソ。私がPythonを始めようとしているすべての人(上級者とか逸般人とか言われる人を除く)に言っていることです。 Pythonをやりましょう。 プログラミング初心者が一番最初に触るのに適している言語はPython…そんなわけ無いですね。JavaScriptだと思います(個人の感想です)。 JavaScriptについては別の回でやります。 まあPython人気ですし。やりましょう。図1:Googleトレンド「プログラミング言語」関連トピックの人気順5位がPython(2021年6月28日現在)←執筆をサボってたのがバレちゃう! Pythonが初心者向けである理由ググれ。というのもなんなのでちょっとぐらい僕の見解を話します。動的型付け言語である言語の特性の話ですが、型を意識してプログラムを書くというのは知識なしでは難しいと思います。 単にモノを作りたい、というだけであれば別に静的型付けに拘る必要はないですし、余計な難点が取り払われると考えることができます。 本物の初心者はエラーログを読めないことが多い[独自研究]のでそっちのほうが楽です。実はそんなに新しくないPythonはここ数年で[いつ?]急速に流行りだした言語ではありますが、Python 2.0(廃止済み)が2000年に、Python 3.0が2008年にリリースされています。 …いや言うて新しいやん、Go,Rust,Kotlinあたりと同じぐらいの時期やん、と思われるかもしれません。 が、それは言語としての話であって処理系としての話でいうとPython 3がだいぶ早いです。Go 1は2012年3月28日、Rust 1.0.0は2015年5月16日、Kotlin 1.0.0は2016年2月15日です。 対してCPython v3.0は2008年12月4日です。 まあ大幅な仕様変更が有ったとは言え、随分前からv2.xの処理系は有ったわけですから当然っちゃ当然ですね。 余談ですが、今は見る影もないCPython v1.0.1は1994年2月16日にリリースされました。Gitが生まれたのが2005年といえば相当昔なのがわかると思います。 利用者が多いという点も、情報が多いことに寄与していると思われます。 公式リファレンスも充実しています(もっとも、競プロだから公式リファレンスと多少のモジュールだけ見てれば十分という話ですが)。 競プロにおいてPythonが微妙な点遅い最大の欠点と言っても過言ではない。そう、このCPythonというインタプリタは普通に使うと遅いです。 それはもうとにかく遅い。ABC182-E の言語別実行時間分布 Rust はっや pic.twitter.com/mh6aXMAxNo— scol (@scol_kp) November 9, 2020 上記tweetの一番右が多分CPython 3だと思いますが、AC者滅茶苦茶少ないですよね。なぜかと言えば普通に書くと遅いから。 Python 3自体の使用者はかなり多いのですが(C++に次いで2番目[要出典])、 ユーザーの大半はPyPyというCPythonとは別のインタプリタを使っています。 CPythonで動くコードをほとんどそのまんま動かせて、しかも早いのが素晴らしいところです。 AtCoderで未だにPyPy 7.3.0(Python 3.6)が使われているのが玉に瑕ですが、次の言語アップデートを気長に待ちましょう。Off-side Ruleサッカーの話ではなくて。ここでは、左端から離れる(off-side)規則のことです。インデントでブロックを表します。 対になる概念はCurly-bracket programming(中括弧でブロックを表す)。 Pythonという言語の設計思想は調べてもイマイチわかりませんが、 一つ言えそうなことは「プログラムが動くためには、美しく有るべき(SHOULD)。」ということです。動いてはならない(MUST NOT)、まで強くはないです。 ついでに、現在のPythonの言語仕様は PEP(Python Enhancement Proposal; Pythonをよりよくする提案)という提案を多数取り込むことで出来上がっています。 マージされたPEPの中でも最も有名なのはPEP8でしょう。 Pythonの生みの親であるGuido van Rossumを含む3人によって提案された、コードスタイルについての規則です。 その中では、インデントの推奨サイズ(半角スペース4個、2個でも8個でも、タブスペースでもない)や1行の推奨最大文字数まで指定されています。 それで、それの何が問題かと言えば、コードをコピペしたときにインデントが崩れたとして、それを自動で直すことができません。 コピペライブラリを貼り付ける場合にわざわざインデントを手動で直さなければならず、不便きわまりありません。 中括弧を使わなくともブロック末尾にendキーワードを置くRubyでは問題にならないのですが…… 本編お待たせしました。環境構築について話していきます。 例によって、#1 共通編を前提としますが、 使っているHeadless CMSサービスの都合上、<details> <summary>要素とかidによるページ内リンクが使えずごちゃごちゃしているので、 そのうち大幅改版してDockerfileでも作ることにします。乞うご期待。 正規表現でうまいこと置換すれば任意のHTML要素を置けるとかそういうことを言わない。 さて、Pythonの環境についてですが、一応CPythonとPyPy両方の説明をします。 速度的にはPyPyのほうが手抜きして高速化できるのですが、AtCoderのPyPyではNumPy等の便利なライブラリが使用できません。 CPythonで使えるライブラリはここのQiita記事に書いてありましたが、実際使えそうなのはNumPy、Numba、NetworkXぐらいでしょうか。SciPyも使える時があるのかな。 私は標準ライブラリしか使わないので基本PyPyです。PyPyUbuntuでPyPy3をインストールする方法は複数あります。aptを利用してUbuntu公式リポジトリのpypy3/focal 7.3.1+dfsg-4をインストールする公式で配布されているPyPy3 7.3.0のビルドをダウンロードするpypy.orgからPyPy3 7.3.0のソースをダウンロードしてビルドする手っ取り早い1と2の方法を紹介します(PyPyは公式でも自分でビルドすることは推奨されていないので)。方法1 apt install説明するまでもないような気がしますが。$ sudo apt install pypy3 $ pypy3 -Vはい。方法2 ビルド済みのPyPyをダウンロードこれもtarをダウンロードしてきて展開して適切な場所に配置するだけです。簡単ですね。$ sudo apt install wget $ cd /tmp $ wget https://downloads.python.org/pypy/pypy3.6-v7.3.0-linux64.tar.bz2 $ sudo tar vxjf pypy3.6-v7.3.0-linux64.tar.bz2 -C /usr/local/lib $ cd /usr/local/bin $ sudo ln -s ../lib/pypy3.6-v7.3.0-linux64/bin/pypy3 pypy3 $ pypy3 -VCPythonAtCoderで2021年11月現在採用されているCPythonのバージョンは3.8.2です。aptを利用してUbuntu公式リポジトリのpython3/focal,now 3.8.2-0ubuntu2をインストールするpython.orgからPython 3.8.2のソースをダウンロードしてビルドするとまあPyPyと概ね同じですが、Linux向けビルド済みパッケージを公式が配布していないので、 Ubuntuからの供給が絶たれれば自分でビルドする他なくなります。方法1 apt install$ sudo apt install python3 $ python3 -Vはい。方法2 ソースからビルドする時間がかかるのであんまりしたくありませんが、一応。$ cd /tmp $ sudo apt install wget xz-utils gcc make zlib1g-dev libssl-dev libbz2-dev libffi-dev $ wget https://www.python.org/ftp/python/3.8.2/Python-3.8.2.tar.xz $ tar vxJf Python-3.8.2.tar.xz $ ./configure --prefix="$HOME/.local" $ make $ make test $ sudo make install $ echo "PATH=$HOME/.local/bin:\$PATH" > ~/.bashrc && source ~/.bashrc $ python3 -VライブラリのインストールCPythonはいくつかライブラリが入っているという話をしたので、一応入れておきましょう。$ pip3 install networkx numba numpy scipy何も考えることはないですね。 こちらとしてはインストールに難儀しました。 ./configure時に--prefixオプションを指定しないと、make install時に/usr/local/binに書き込もうとするので権限がなく、sudo make installするとpip installがrootでしか使えませんでした。 また、libssl-devを入れないとそもそもpip installできないし、libbz2-devがないとNetworkXが、libffi-devがないとがNumbaとSciPyが使えませんでした。 これでお好きなようにPythonのコードを書くことができます。VSCodeの拡張機能PythonPylancepython-snippetsVisual Studio IntelliCodeあたりを入れとけばいいんじゃないですかね。知らんけど。

> 内容を見る

プログラミング Polygon
プログラミング

LaravelをCloud Runにデプロイして効率よくバックエンドを開発しよう!

何円ぐらいで済むの? 以下の手順で60.77時間、動かしてみました。リージョンは全て東京(asia-northeast1)です。 結果MySQL 実行: 97円MySQL ストレージ: 11円ストレージ(NA-APAC間の通信): 5円以上、113円でした。 一時間1.85円として計算すると、1ヶ月で1338円になります。 お詫びすいません、ケチれませんでした!!!!! ただ、GitHubからデプロイすることで効率の良い開発スケール他GCPサービスとの円滑な連携が可能になる点は見逃せません。余裕のある方は試してみてください。 下準備GCPのセットアップgcloud CLIをインストールしておく。環境によって手順が異なるので省略。 プロジェクトを作り、支払いアカウントを紐づける。 gcloud auth loginまずはログインする。 PROJECT_NAME=(表示上のプロジェクトネーム) PROJECT_ID=$(gcloud projects list --format 'value(projectId)' --filter name=$PROJECT_NAME) export PROJECT_ID gcloud config set project $PROJECT_ID gcloud services enable \     containerregistry.googleapis.com \     cloudresourcemanager.googleapis.com \     iam.googleapis.com「表示上のプロジェクトネーム」はGCPの画面に出ているプロジェクト名。プロジェクトをセットし、APIを有効化する。 Terraformのインストールいつか使うんだろうなーと思っていた。入れてなかったのでここで入れておく。 brew install tfenv tfenv install 0.12.6 tfenv use 0.12.6tfenvを使うのは、以下のテンプレートが0.12.6を想定していたから。随時変更しよう。 ステップ1. テンプレートを準備自分でやろうとしたがあまりにめんどくさかったので、以下のテンプレートを使う。 https://github.com/villers/laravel-cloud-run-sql これをフォークしよう。 クローンしてビルドgit clone git@github.com:あなたのユーザー名/laravel-cloud-run-sql.git cd laravel-cloud-run-sql docker-compose build euをasiaに東京で動かすので、レポジトリ内のeu.gcr.ioをasia.gcr.io に置換。 ローカルで試すdocker-compose up 起動の様子。なんでもDockerでやれば、なんかすごいことやってるみたいに見える。 docker-compose run --rm app php artisan migrate --seed マイグレーションする。 http://localhost:8080/ で動作確認しよう。 ステップ2. GCPにコンテナをアップするサービスアカウントを用意するサービスアカウントを作り、Owner権限を与える。キーをダウンロードしてterraform-key.json として保存し、環境変数に設定、サービスアカウントとして認証する。それらが以下のコマンドでできる。ありがとうgcloud CLI! gcloud iam service-accounts create deployer \     --display-name "Terraformのサービスアカウント" gcloud projects add-iam-policy-binding ${PROJECT_ID} \   --member serviceAccount:deployer@${PROJECT_ID}.iam.gserviceaccount.com \   --role roles/owner gcloud iam service-accounts keys create ~/terraform-key.json \   --iam-account deployer@${PROJECT_ID}.iam.gserviceaccount.com export GOOGLE_APPLICATION_CREDENTIALS=~/terraform-key.json gcloud auth activate-service-account --key-file=$GOOGLE_APPLICATION_CREDENTIALS ビルド(M1注意)export SERVICE_NAME=laravel-app-service docker build --platform linux/amd64 -f .cloud/docker/php/Dockerfile -t asia.gcr.io/${PROJECT_ID}/${SERVICE_NAME} . 上記コマンドでアジア用タグをつけたイメージをビルドする。間違ったタグをつけたりしたら、docker images からのdocker rmi [ID] で消せる。それか、Docker Desktopのイメージ画面を使う。 --platform linux/amd64 はM1用。ARMでビルドしてもデプロイで死ぬ。このせいで数時間が無駄になった。 レジストリにプッシュgcloud auth configure-docker docker push asia.gcr.io/${PROJECT_ID}/${SERVICE_NAME}コンテナレジストリにログインして、さっきのをプッシュする。 ステップ3. 自動でCloud SQLとLaravelをデプロイTerraform初期化cd .cloud/terraform terraform init ついに初回デプロイexport REGION=asia-northeast1 export INSTANCE_NAME=laravel-app-service terraform apply \   -var "region=${REGION}" \   -var "service=${SERVICE_NAME}" \   -var "project=${PROJECT_ID}" \   -var "instance_name=${INSTANCE_NAME}" Do you want to perform these actions?   Terraform will perform the actions described above.   Only 'yes' will be accepted to approve.   Enter a value: yesと打てば、Secret Managerでキーを作り`laravel`というサービスアカウントができCloud SQLにMySQLをセットアップCloud RunでLaravelをデプロイの4つが行われる。 Cloud SQLの部分でかなり時間がかかる。 Apply complete! Resources: 26 added, 0 changed, 0 destroyed. Outputs: result =     The laravel-app-service is now running at https://<ここにURL>     If you haven't deployed this service before, you will need to perform the initial database migrations:     cd ../..     gcloud builds submit --project <ここにプロジェクトID> --config .cloudbuild/build-migrate-deploy.yaml \         --substitutions _APP_ENV=dev,_APP_DEBUG=true,_REGION=asia-northeast1,_INSTANCE_NAME=laravel-app-service,_SERVICE=laravel-app-service     The username and password are stored in these secrets:     gcloud secrets versions access latest --secret DATABASE_URL     ✨ 終わるとこんなんが出てくる。 Cloud Runの画面でもURLが確認できる。マイグレーションは後でやる。 GitHubと連動してビルド・デプロイさせる実はTerraformを使うのは初回だけでいい。あとはCloud Buildを活用しよう。 ルートにcloudbuild.yaml を作る。 cloudbuild.yamlsteps:   - id: 'build_and_push'     name: 'gcr.io/kaniko-project/executor:latest'     args:       [         # https://github.com/GoogleContainerTools/kaniko/issues/1427         "--dockerfile=.cloud/docker/php/Dockerfile",         "--cache=true",         "--cache-ttl=6h",         "--destination=asia.gcr.io/${PROJECT_ID}/${_SERVICE}",       ]      - id: 'migrate'     name: 'gcr.io/google-appengine/exec-wrapper'     args: ['-i', 'asia.gcr.io/${PROJECT_ID}/${_SERVICE}',            '-s', '${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME}',            '-e', 'PROJECT_ID=${PROJECT_ID}',            '-e', 'APP_ENV=${_APP_ENV}',            '-e', 'APP_DEBUG=${_APP_DEBUG}',            '--', 'php', 'artisan', 'migrate']   - id: 'deploy'     name: 'gcr.io/cloud-builders/gcloud'     args: ["run", "deploy", "${_SERVICE}",            "--platform", "managed",            "--region", "${_REGION}",            "--image", "asia.gcr.io/$PROJECT_ID/${_SERVICE}"] options:   substitutionOption: ALLOW_LOOSE substitutions:   _APP_ENV: prod   _APP_DEBUG: "false"   _SERVICE: laravel-app-service   _REGION: asia-northeast1   _INSTANCE_NAME: laravel-app-service ビルドのトリガー設定を変更(重要) 次に、Cloud Runのページを開く。 このボタンでビルドのトリガーを作れる。 GitHubのレポジトリと接続すればいいが、Dockerfile は間違いなので修正が必要。 「EDIT CONTINUOUS DEPLOYMENT」から設定を開き、LocationをRepository に変える。 Cloud Build configuration file (yaml or json) を選択する。/cloudbuild.yaml になっているか確認。ブランチ名を変えた場合は、適宜トリガーの設定も変える。 プッシュすればビルド、プッシュ、マイグレーション、デプロイがされる。こちらのリンクからビルド履歴が見れる。 初回は時間がかかるが... 2回目以降はKanikoがキャッシュを活用してくれる。大体二分で終わる。 え? FTPで差分をアップした方が早い? 確かにそうかもしれない。だが、全部自動化バージョンを戻せる環境を簡単に再現という点で明らかに効率がいい。積極的に活用していこう。

> 内容を見る

NoImage Polygon
プログラミング

Privateの仕様を勘違いしてました

プログラミングできる人からしたら何言ってんだという内容かもしれませんが、結構ショックを受けたので残しておきます 先日、Kotlinのコードを読んでいたら、こんな感じのコードに出会いました class Foo(private var value: String) { operator fun plus(other: Foo): Foo { return Foo(this.value + other.value) } } パッと見普通のコードなのですが、このコードのために頭を抱えさせられました というのも、別のインスタンスのprivateプロパティにアクセスするコードが書かれているからです class Foo(private var value: String) { operator fun plus(other: Foo): Foo { return Foo(this.value + other.value) } } 例えば以下のようなコードが書けてしまいます fun main() { val foo1 = Foo("Hello") val foo2 = Foo("World") println(foo1 + foo2) } 結果 Foo: { value : HelloWorld } このコードでは、foo1とfoo2は、もともとのクラスは同じですが、全く別のインスタンスです。 しかし、このように書くことで、別のインスタンスのprivateなプロパティにもアクセスできるようになっています そこで調べてみたところ、以下の記述がありました(参照先) private はそのクラス内(そのすべてのメンバーを含む)でのみ見える よく見るとインスタンスではなく、クラス内のみアクセス可能と書いていました つまり同一のクラスからできたインスタンスだとアクセス可能らしいです だから最初のコードでもコンパイル&実行できるということらしいです しかし、ここでいくつかの疑問が出てきます。そこでそこらへんも調べてみました Privateなメソッド(関数)でも同じなのか 同じように動くようです(参考先) サブクラスだとどうなるのか 動きません 仮に、サブクラスも参照可能にするprotectedを利用しても不可能でした open class Foo(protected var value: String) class Bar(private val name: String): Foo(name){ // 引数のクラスの参照はNG fun ng(other: Foo) { println(other.value) } // 自身の親の参照はOK fun ok() { println(value) } } 拡張関数だとどうなるか 無理です open class Foo(private var value: String) // NG fun Foo.print(){ println(this.value) } これはJavaにコンパイルされたときのコードを見ればわかります public static final void print(@NotNull Foo $this$print) {   // Something... } Javaには拡張関数は存在しないため、引数としてクラスを渡して、それで処理しています ですので、privateプロパティにはアクセスできないので、動きません 自身のインスタンスだけアクセスできるようにする方法はないのか 少なくとも公式からは出されていない感じがします(privateが一番厳しい制約とのこと) Kotlinの場合、DelegateとかReflectionを使えば書ける気もしないこともないですが、書いたところでコンパイルエラーになりません したがってほとんど意味を成しません ですので、コメントやアノテーションなどで、分かるようにするのが精いっぱいだと思います 他の言語だとどうか C# using System; public class Program { public static void Main() { Foo foo1 = new Foo("Hello"); Foo foo2 = new Foo("World"); Console.WriteLine(foo1.getValue(foo2)); } } public class Foo { String value; public Foo(String value) { this.value = value; } public String getValue(Foo other) { return other.value; } } 結果 World 動いています PHP <?php $foo1 = new Foo("Hello"); $foo2 = new Foo("World"); print $foo1->getValue($foo2); class Foo {     private string $value;          function __construct(string $value)     {         $this->value = $value;     }          function getValue(Foo $other): string     {         return $other->value;     } } 結果 World 動いています ですので、Java、C#、PHPで動くため、おそらくほぼすべてのプログラミング言語はこの仕様だと考えられます まとめ 最初見たときは本当に驚きましたが、冷静になって考えるとこちらの方が理にかなっていますね 同一インスタンスじゃないとアクセスできない!としないといけない場面てほとんどないでしょうし むしろこちらの方が、クラスの役割をきっちりかけていい感じがします 今までずっと勘違いしてきたことを今更知って本当に恥ずかしいです・・・

> 内容を見る

プログラミング Polygon
プログラミング

VSCodeのVueでpropsの型チェックをする方法 (TypeScript)

注意書き 以下の内容はVSCode 1.16.2Vetur v0.35.0Nuxt v3.0.0-27252999.d2cc9e4 (Vue: 3.2.20)Next 11.1.2 (React: 17.0.2)時点の内容です。あと、当然ですが全てTypeScriptです。 Nuxt3はまだベータ版ですが、起動が早い + TypeScriptが使いやすいので皆さんも試してみてください。 Vueでpropsの型チェックをさせる方法ステップ1. Veturを入れるhttps://github.com/vuejs/vetur これがないと話にならないです。 ステップ2. 設定を変えるなぜかデフォルトで型チェックできないので、settings.json に以下の設定を追記します。 { (前略) "vetur.validation.templateProps": true, "vetur.experimental.templateInterpolationService": true, (後略) } ステップ3. コンポーネントを作成する<template> <div> <h2>{{ name }}様</h2> <div v-if="age"> <b>{{ age }}歳なので</b> <span>わいの{{ age && age / 20 }}倍!</span> </div> </div> </template> <script lang="ts"> export default { name: "TestComponent", props: { name: { type: String, required: true, }, age: { type: Number, required: false, }, }, } </script> name は文字列(必須)で、年齢は数値(任意)です。 「名前と年齢を受け取って、年齢があれば俺の年齢と比較する」という意味不明なコンポーネントです。 ステップ4. ページで使ってみる<template> <div> <TestComponent name="aaa" :age="100" /> </div> </template> <script lang="ts"> import TestComponent from "../components/TestComponent.vue" export default { components: { TestComponent }, } </script> こんな感じでページで使います。 できましたね。 ステップ5. エラーを起こしてみる<TestComponent name="aaa" age="100" /> 試しにこう書いてみてください。 Type 'string' is not assignable to type 'number'. Veturから怒られるはずです。なぜならage="100" が文字列の「100」を渡しているからです。 が、templateInterpolationService がまだ「experimantal」なので、エラーが出る場所がおかしいです。 ちょっと待って!Reactで書けばいいやんあのですね、なんで単なる自動補完に、拡張機能と設定がわざわざ必要なんですか? Reactで同じことをやってみてください。何も設定がいらないです。 ステップ1. コンポーネントを書くinterface Props { name: string; age?: number; } const TestComponent: React.VFC<Props> = (props) => ( <div> <h2>{props.name}様</h2> {props.age && ( <div> <b>{props.age}歳なので</b> <span>わいの{props.age / 20}倍!</span> </div> )} </div> ); export default TestComponent; めっちゃ短くなります。 型の定義も、ただTypeScriptのinterfaceを書けばいいのです。Vueのprops よりスッキリしていて見やすいでしょう? Next.jsのページで使うimport TestComponent from '@/components/TestComponent'; import { NextPage } from 'next'; const TestPage: NextPage = () => ( <div> <TestComponent name="aaa" age={100} /> </div> ); export default TestPage; もしage に数値以外を渡すと、その場でエラーが出ます。繰り返しますが、拡張機能や設定は必要ありません。 Vueはpropsだけで記述量が多すぎるそもそもですね、たかが「名前と年齢を出す」だけで記述量が多すぎるんですよ。 export default { name: "TestComponent", props: { name: { type: String, required: true, }, age: { type: Number, required: false, }, }, } これ、「nameとageを受け取る」ってだけですよ。長すぎるでしょ。 interface Props { name: string; age?: number; } const TestComponent: React.VFC<Props> = (props) => ( <div> <h2>{props.name}様</h2> {props.age && ( <div> <b>{props.age}歳なので</b> <span>わいの{props.age / 20}倍!</span> </div> )} </div> ); export default TestComponent; さっきも書きましたけど、Reactはテンプレート自体を含めてこの長さですからね。絶対こっちの方が時間の節約になります。 以上、俺がReactを使う理由の記事でした。 --- あれ?なんの記事だったっけ? 思い出した!自動補完や! 以上、VSCodeのVueでpropsの型チェックをさせる方法でした。

> 内容を見る

プログラミング Polygon
プログラミング

岡大の新工学部HPのヘッダーを無理やり修正する

岡山大学工学部の新ホームページには、表示上の欠陥があります。 情報・電気・数理データサイエンス系とかいう欲張りで長すぎる名前が、「令和3年度〜新生工学部Webサイト」と被ってしまっています。 この現象は、画面の横幅が412px を下回った時に起こります。画像で記述されたロゴがどちらも透過画像であるため、文字が読みづらくなります。 本来は、HPを作成した岡山大学自然系研究科等事務部総務課に連絡すべきですし、今からしようと思いますが、成績が悪くて連絡しづらい夏季休業期間中ということで無理やり修正します。 修正する上での気を付けたことJavaScriptは使わない見た目の問題を後付けスクリプトでいじることはできるだけ避けたい行為です。 疑似要素を活用する既存の要素のプロパティ変更には限界があります。 例えば今回の場合、ロゴがpng画像なので、文字列を改行させるわけにはいきません。 そこで、before/after擬似要素を追加して、「令和3年度〜新生工学部Webサイト」を無理やり書き込みます。 Stylusを入れようStylusのダウンロード Google Chrome拡張の「Stylus」を入れます。Firefoxにも対応していますが、リンクは割愛します。 CSS:root {     --sublogo_width: 35vw;     --header_height: 37px; } #header .inr #logo {     position: relative; } #header .inr #logo a {     z-index: 4; } /* 本来のサブロゴは消す */ #header .inr .sp_subLOGO {     display: none; } h1#logo::after {     position: absolute;     top: 0;     display: none;     height: var(--header_height);     content: '令和3年度~新生工学部Webサイト';     align-items: center;     justify-content: flex-end;     right: 60px;     width: var(--sublogo_width);     font-size: 1rem;     text-align: right;     font-family: '游明朝','Yu Mincho',YuMincho,'Hiragino Mincho Pro',serif;     z-index: 3; } @media screen and (min-width: 310px) and (max-width: 1000px) {     h1#logo::after {         display: flex;     } } sublogo_width の35vw は完全に適当に決めているので、ベターな値があるかもしれません。 結果 疑似要素に「令和3年度〜新生工学部Webサイト」を書いているため、その横幅に応じて改行がされます。 スマホの横幅だといい感じに改行されて、メインロゴと被りません。なお、あまりに狭い場合はメディアクエリでサブロゴを隠します。 align-items: center; のおかげでいい感じに中央寄せできています。数年前にはできなかった選択肢。ありがとうflexbox!

> 内容を見る

プログラミング Polygon
プログラミング

コミットメッセージとバージョンアップとタグ付けと更新履歴を自動化しよう

コミットメッセージの構文を統一し、レポジトリのCHANGELOG.mdの更新とタグ付けを自動化します。 参考https://itnext.io/how-to-automate-versioning-and-publication-of-an-npm-package-233e8757a526https://numb86-tech.hatenablog.com/entry/2019/07/12/212053https://github.com/conventional-changelog/commitlint この記事でやることコミットメッセージの記法を統一更新履歴とタグの自動化 前提知識Conventional Commitshttps://www.conventionalcommits.org/ja/v1.0.0/ Conventional Commits の仕様はコミットメッセージのための軽量の規約です。 明示的なコミット履歴を作成するための簡単なルールを提供します。この規則に従うことで自動化ツールの導入を簡単にします。 コミットメッセージで機能追加・修正・破壊的変更などを説明することで、この規約は SemVer と協調動作します。 SemVerは流石に知ってたんですが、私はこれを知らずに思考停止でコミットメッセージ書いてたんですよ。いけませんね。 VSCodeにConventional Commitsの拡張機能を入れるhttps://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits ▲これを入れます。 レポジトリパネルに丸マークが出ます。 commitlintのhookを用意する次に、コミットメッセージを制限します。 yarn add -D husky @commitlint/{config-conventional,cli} echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.jsWindowsでは @commitlint/config-conventional @commitlint/cli という風に分けて書きます。 commitlint.config.js がルートに作成されているか確認してください。 yarn husky install npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'上記コマンドでhookを用意します。 package.json{ (...略) "config": { "commitizen": { "path": "cz-conventional-changelog" } }, "husky": { "hooks": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } } }package.json を編集。これでcommit-msg のhookが準備できました。 この状態で適当なコミットをすると、「Conventional Commitsの記法になってない」という理由で怒られます。 はじめてのConventional Commit 変更をステージしてから、さっきの丸マークを押します。 あとは画面の指示に従って選択肢を選びます。まずは「タイプ」。 次に「スコープ」。コミットが関係する領域です。一回作れば使いまわせます。 その後「絵文字」「概要」「詳細」「関係するissue」の順番で書いていきます。 Conventional Commitの記法に従って、以下のメッセージが生成されます。<タイプ>(スコープ):(絵文字) 概要 詳細 関係するissueissueを閉じたいなら、最後(手順6/6)にclose #1 と書けばいいんです。 standard-versionで更新履歴とタグを自動化yarn add -D standard-versionめっちゃ便利な神パッケージです。CHANGELOG.md を自動で書いてくれます。 package.json{ (略) "scripts": { "release": "standard-version", "push": "git push --follow-tags origin main" (略) }, (略) }releaseコマンドで作業をトリガーします。 初回リリースyarn run release --first-release初回は--first-release オプションをつけます。 yarn run release # yarn run v1.22.11 # $ standard-version # ✔ bumping version in package.json from 0.1.1 to 0.1.2 # ✔ outputting changes to CHANGELOG.md # ✔ committing package.json and CHANGELOG.md # ✔ tagging release v0.1.2 # ℹ Run `git push --follow-tags origin main` to publish # ✨  Done in 4.70sこれでバージョンが上がってタグが付き、更新履歴が自動で更新されます。 CHANGELOG.md# Changelog All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. ### [0.1.3](https://github.com/sasigume/project-21600/compare/v0.1.2...v0.1.3) (2021-09-16) ### [0.1.2](https://github.com/sasigume/project-21600/compare/v0.1.1...v0.1.2) (2021-09-16) ### Bug Fixes * **lint:** :ambulance: commitlintの余計な打ち間違いを削除 ([8aa522f](https://github.com/sasigume/project-21600/commit/8aa522f1333f2fd2df60c8d2e285b8e7b8d60e2a)), closes [#1](https://github.com/sasigume/project-21600/issues/1)自動生成された結果です。改行が無駄に多い気がしますね。 バージョン指定なおyarn run release --release-as x.x.x でバージョン指定もできます。 最後にyarn run push でタグ付きでプッシュしましょう。

> 内容を見る

プログラミング Polygon
プログラミング

Jetpack Composeで、Mutable系統をStateに登録したらあかん

タイトルで書いてあることを悟るまでに数日潰したので、怒鳴り散らしながらメモとして残します。 Jetpack ComposeとはAndroid開発において新たな時代の到来を感じさせるUI Toolkitです。 Androidは十数年前に登場して以降、様々なアプリが開発されてきています。 その影響もあり、Android開発を行うときは十数年前のクソみたいな古い風習が残っています。 例えばUIを作成するときです。 Androidでは命令型UIを採用しており、XMLと呼ばれる言語で書いていく必要がありました。 以下のような奴です<?xml version="1.0" encoding="utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/LinearLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:background="@android:color/white" android:elevation="4dp" android:orientation="horizontal"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/title" android:layout_weight="1" android:gravity="center" android:layout_gravity="center_vertical" android:textSize="30sp" android:layout_height="wrap_content" android:layout_width="0dp" android:clickable="true" android:text="@string/title" android:focusable="true" /> <androidx.appcompat.widget.AppCompatImageButton android:id="@+id/delete_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_delete_title" android:contentDescription="@string/delete" /> </androidx.appcompat.widget.LinearLayoutCompat> こんなコードから以下のようなUIが出力されます こんなのコードだけ見ても何を表しているのか、慣れていない人にとってはさっぱりわかりません。慣れててもよくわかりません。 これだけならいいのですが、ここからデータを表示するとなると、KotlinやJavaでコードを書く必要があります。 その書き方がかなりめんどくさいわけです。例えば、以下のような感じになります。 val binding = ChecklistTitleBinding.inflate(inflater, parent, false) val title: TextView = binding.title //要素の文字の部分の設定 title.apply { text = "Hello World" textSize = getTextSize(context).toFloat() //タイトル名をクリックした際の処理 setOnClickListener { clickTitleOnListener(title.text.toString()) } } この通り、部品一つずつ指定して書いていく必要があります。 これで、部品の名前とかはXMLで書いているのでそっちを見ないといけないし、指定するのを忘れていたらバグにつながります。 そこでJetpack Composeの出番です。 これは最近はやっている宣言的UIを採用しており、パッと見たらわかる感じになっています。 例えば、以下のように書くと、先ほど紹介したコードと同じことができます。 @Composable fun LazyCard(text: String = "Hello World"){ Card( modifier = Modifier .padding(10.dp) .fillMaxWidth() ) { Row( modifier = Modifier.padding(12.dp) ) { Text( text = text, modifier = Modifier.weight(1f) ) Icon( painter = painterResource(R.drawable.ic_baseline_delete_24), contentDescription = null ) } } } 以下のような感じですね これだと、コードを見ただけで、先ほどよりは断然分かりやすいと思います。 どのような見た目をしているかだったり、どこにデータが流れていくかであったりが分かりやすいので非常に使いやすいです。 このように、Jetpack Composeは、Androidの新時代を感じさせるUI Toolkitになっています。 本題やっと本題です。 Kotlinには複数のデータを扱うために、標準ライブラリの中にいくつかの型(class)が存在します 例えば配列を扱うためのList型、複数のPair型(keyとvalueを持った型)を扱うためのMap型などがそれにあたります。 これらの型は、一度値を作成したら値を追加、削除することができません しかし、これらを継承した型である、MutableList型、MutableMap型等、Mutable系統であれば可能ですval list = listOf("は", "ひ", "ふ") //コンパイルエラー list.add("へ") val mutableList = mutableListOf("は", "ひ", "ふ") //これはOK mutableList.add("へ") 結構扱いやすく、私もよく利用している型です。 そのため、Jetpack Composeにおいても何気なく使ったことが運の尽きでした。 ある日、こんな感じの、+ボタンを押したらカードが追加されていくアプリを作成しました。 ソースコードは以下の通りです。@Composable fun List(){ //副作用 var list by remember { mutableStateOf((0..3).toMutableList()) } //UI部分 LazyColumn(Modifier.fillMaxSize()){ item{ //カード表示部分 for(i in list){ LazyCard(text = i.toString()) } } item { //ボタン AddButton( //ボタンを押したときの処理 onClick = { val tempList = list tempList.add(4) list = tempList } ) } } } (副作用ってなんやねんて話ですが、話し出すと記事が一つできるような内容ですので省きますが、簡単に言うと、「関数外のところに影響を及ぼす部分」のことです。) このremember{}の部分が何をしているかというと、画面に表示するためのMutableListを保存しています。 rememberのところでState型を保有しておき、それを返してくれます。 これを変数に入れておき、値が変更されると、UIを更新してくれる優れものです。 今回の場合ですと、Listの中身が追加されれば、表示されるリストの数も追加されて以下のようになるはずです。 そして、画面内の+ボタンを押すと、Listの中身が追加されるようになっています。//ボタン AddButton( //ボタンを押したときの処理 onClick = { //別の変数に代入 val tempList = list //変数を追加   tempList.add(4) //listに反映 list = tempList } )つまり、このボタンを押せば増えてくれるはずです。 しかし、実際には増えてくれません。 この挙動のせいで数日間頭を抱える事態に陥ってしまいました 原因結論から書くと、rememberで渡された値が参照渡しになっていることでした。 私の意図した動きは以下の通りです//副作用の部分 var list by remember { mutableStateOf((0..3).toMutableList()) } AddButton( onClick = { //別の変数に、実際の値のコピーを代入 val tempList = list //tempListのリストに、変数を追加   tempList.add(4) //本体のlistに反映(ここで差分を検知してUIの再描画が走る) list = tempList } ) しかし、実際にはこう動いていました。//副作用の部分 var list by remember { mutableStateOf((0..3).toMutableList()) } AddButton( onClick = { //別の変数に、listの参照先を代入 val tempList = list //tempListのリストに、変数を追加(ここで、listの方にも変数が追加される。この追加方法ではUIの再描画が走らない)   tempList.add(4) //本体のlistとtempListの両方に値が追加されたせいで、差分が一切ないため、UIの再描画が走らない list = tempList } ) いろいろと調べてると、どうやらKotlin(というよりJava)では、クラス型は参照渡しを採用しているらしいです。 http://syrinx.q.t.u-tokyo.ac.jp/tori/java/basic/entity_ref.html Mutable系統もクラス型なので参照渡しということですね。 解決策値を新しく作って、ぶち込めば解決です。//副作用の部分 var list by remember { mutableStateOf((0..3).toList()) } AddButton( onClick = { list = list + listOf(list.size) } ) listに、新しくList型を作り直して、それをぶち込んでいます。 ここで重要なのが、Mutable型の特徴であるadd()関数では差分を検知してくれないということです。 ですので、Mutable型を使うことに対するメリットがないばかりか、意図せず使ってしまい、バグを誘発する危険性があります。 だからこそ、Mutable型をStateの部分(rememberのところ)では避けて、普通の型を使うべきでしょう。 以上 しかし、こんな記事俺以外に誰が見るんだろうか・・・

> 内容を見る