無償版G Suiteが廃止されるのでAWS SESとDockerでメール送受信システムを作った

20日木曜、大きなニュースが飛び込んできました。2012年に提供が終了して、そのまま9年近く利用できていた旧無償版G Suiteが7月1日に廃止されてしまいます。

www.itmedia.co.jp

自分も黎明の頃から勉強会のメールアドレスや、友人に独自ドメインアドレスを提供したりと、便利に使わせてもらってきていたのですが、現状の利用規模的に個人ユースで月数千円の追加支出になるのはさすがに許容できません。既にGoogle OneやYoutube PremiumなどGoogleにはかなりお金を払っているので、これ以上Googleにお金取られるのは気分的にヤダというのもあります…。

そこでまじめに検討した結果、独自にメール受信システムを作ることにしました。

結果、月々100円以内におさまるシステムに仕上がりました。

2022/05/05(Thr) 追記

本記事では当初Oracle Cloud Infrastructureを使って使用していましたが、課金状態にあるにも関わらず突如理由もなくアカウントを停止されるという事態が発生しているとのことです。

zenn.dev

幸い僕の手元の環境は問題ない状態でしたが、このような状況では安心して使いつづけられませんので、OCIのコンピュートで建てていたVMAWS Lightsailに移しました。 それに伴い本記事は改題しておりますが、中の説明は引き続きOCIとなっています。

これにより、3.5ドル/月が加算され、トータルでは500円程度の維持費となりそうです。

要件検討

まず、要件を洗い出しました。

続きを読む

Apple USB-C Digital AV Multiport アダプタはアップデートされている

f:id:iseebi:20211029182046j:plain
使い込まれたUSB-C Digital AV Multiport アダプタ

標題の通りなのですが、Apple USB-C Digital AV Multiport アダプタは、Apple純正のUSB-CからUSB-A、HDMI、USB-Cの充電アダプタに変換してくれるアダプタです。MacBookユーザーなら1本は持っているはずです。

僕はMacBook Pro (13-inch, 2016, Four Thunderbolt 3 ports)を買ったときに一緒に買ったアダプタを一緒に使いつづけていましたが、この頃に買ったアダプタをお持ちの方でまだずっと使いつづけている方は買い替えを検討してみてもいいかもしれません。

続きを読む

Google Cloud Tasksから呼び出すApp Engine TaskとGAEバージョンの関係

Google Cloud TasksからApp Engine HTTPタスクでバックグラウンド処理をするアプリケーションを作っている。

トラフィック分割で様子を見ながらリリースしたい場合、App Engine HTTPはどのバージョンのタスクを実行するのかがわからなかったので実験してみた。

前提条件

続きを読む

Automatorアプリケーションでシェルスクリプト+リソースを配りたい

macOS用のツールアプリケーションをインストールする方法がシェルスクリプトだった。ITエンジニア向けだったら良いんですけど、そうでない人が対象だったのでさすがにGUIが必要で、Automatorで手軽にできないかと思って調べてみました。

実行したいシェルスクリプトを用意する

シェルで実行したい内容を含めたものを、run.sh として作成します。

関連するリソースがある場合は、run.sh と同じ階層において参照できるようにしておきます。Automatorで直接シェルスクリプトを実行すると $0 が取れないが、既にファイルになっているスクリプトが実行されるので $0 も問題なく使えます。

Automatorアプリケーションを作る

Automatorで新規書類を作成するが、このときに「アプリケーション」を選びます。

f:id:iseebi:20200807111236p:plain

次に「AppleScriptを実行」タスクを追加し、以下の内容を入力。

f:id:iseebi:20200807111546p:plain

on run {input, parameters}
    set appPath to path to current application
    set appPOSIXPath to POSIX path of appPath
    set cmd to "sudo " & appPOSIXPath & "Contents/Commands/run.sh"
    do shell script cmd with administrator privileges
end run

今回はツールインストール想定だったので管理者権限をとっています。こうしておくことで管理者権限のパスワード確認ダイアログが表示されますし、キャンセルすればそこで動作が止まります。 4行目から"sudo " &を外して5行目からwith administrator privilegesを取れば一般権限にできます。

なお、Automator上から実行すると、Automator自体の実行パスが取れてしまうので、Automatorの実行ボタンを押しての動作確認はできません。ここまでできたら保存してAutomatorは閉じてOKです。

Automatorにコマンドを含める

保存したAutomatorアプリケーションを右クリックして「パッケージの内容を表示」します。

f:id:iseebi:20200807112100p:plain

Contents/Commands の中に実行したい内容を含めた run.sh を入れます。

f:id:iseebi:20200807112351p:plain

ダイアログを表示する

ユーザーに完了通知をするなどをでダイアログを表示する場合はosascriptコマンドでAppleScriptを書いて実現しますが、ユーザーセッションとは切り離されたところでコマンドが実行されてしまうので、実行しているユーザーのUIDをとってlaunchctl経由で実行する必要があります。こんな感じで関数にしておくと良いでしょう。

#!/bin/bash 

showDialog() {
  user=$(python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");')
  uid=$(id -u "$user")
  launchctl asuser $uid osascript -e "display dialog \"$1\" buttons {\"OK\"}"
}

showDialog "hello"

コード署名をする

他のマシンで動かすにはコード署名をして、Notarizationしておく必要があります。

macOSアプリ開発相当の操作になるので、Apple Developer ProgramもしくはApple Developer Enterprise Programを契約して、Developer ID Application証明書を発行しておくことと、Xcodeのインストールが必要です。

まず、アプリのBundle Identifierを変えておくと良いと思います。「パッケージの内容を表示」して、Contents/Info.plistをXcodeで開き、CFBundleIdentifierを任意のものに変更します。

Bundle Identifierを変更したら、ターミナルでコードサインします。Automatorアプリの場合は、--deepを付けるのがミソらしいです。

$ codesign -s "Developer ID Application: Iseteki Shinjyoushiki (XXXXXXXXXX)" \
    --deep --force --timestamp -o runtime MyAutomatorApp.app

コードサインしたら、zipに圧縮します。なぜかzip -rで圧縮するとNotarizationが失敗したので、Finderの右クリックから圧縮すると良いです。

最後に、Notary Service へ送信します。

$ xcrun altool --notarize-app \
    --username <Apple ID> --password <Apple ID Password> \
    --primary-bundle-id "net.iseteki.MyAutomatorApp" \
    --file MyAutomatorApp.zip

ちなみに、Apple IDに複数のチームが登録されている場合は、--list-providers で先に対象となるチームのProdiverShortNameを調べておいて、--asc-provider引数で指定する必要があります。

$ xcrun altool --list-providers \
    --username <Apple ID> --password <Apple ID Password>
ProviderName              ProviderShortname PublicID                             WWDRTeamID 
------------------------- ----------------- ------------------------------------ ---------- 
Iseteki Shinjyoushiki     XXXXXXXXXX        xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx XXXXXXXXXX 
Shin ISE                  YYYYYYYYYY        xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx YYYYYYYYYY
$ xcrun altool --notarize-app \
    --username <Apple ID> --password <Apple ID Password> \
    --asc-provider XXXXXXXXXX \
    --primary-bundle-id "net.iseteki.MyAutomatorApp" \
    --file MyAutomatorApp.zip

しばらくすると、Notarizationの結果がApple IDのメールアドレスにメールで送られてきます。

※Notarizationについては、このブログでも過去に紹介したことがありますのであわせて読んでみてください。

iseebi.hatenablog.com iseebi.hatenablog.com

参考サイト

GCFをデプロイするGCPプロジェクトのセットアップ

毎回やって毎回引っかかってる気がするのでメモ。本当はTerraformとか使った方が良いんだろうけど。 とりあえず最低限必要なもの、

前提

TypeScript で GCF を作って cloudbuild.yaml を読ませてデプロイする

続きを読む

Firestoreのフィールドにどうしても.を使いたい

Firebaseのフィールド、例えばマップのキーにメールアドレスを使いたいパターンはよくあると思う。例えばこういうの。

permissions: {
  "iseebi@example.com": "read",
  "admin@example.com": "admin"
}

で、これをクエリしようとすると permissions.iseebi@example.com のようになってしまうのだが、このままだとwhereのキーに入れることができない。

// NG
firestore.collection("items")
    .where("permissions.iseebi@example.com", "in", ["read", "write", "admin"])

なんとかエスケープできないか、というのも試したけど、うまくいかず。

このような場合は、FieldPathを使うと良い。

// OK
firestore.collection("items")
    .where(new firebase.firestore.FieldPath(
        "permissions", "iseebi@example.com"), "in", ["read", "write", "admin"])

ただ、そもそもこういうのはベストプラクティスに反するので、避けた方が良い。

参考:

stackoverflow.com

Firebase Hostingでサイト全体をリダイレクトさせる

サイト全体をリダイレクトさせたいということがあると思う。例えば、wwwでメインサイトをホストしているけど、Apexドメインにアクセスしたときもwwwにリダイレクトさせたい、とかの場合だ。他にもちょっと複雑なリダイレクトさせたりもしたいということもあると思う。

これを、Firebase Hostingを使うことで簡単にできたので紹介する。

続きを読む