ハトネコエ Web がくしゅうちょう

プログラミングやサーバー・Web制作、チームマネジメントなど得た技術のまとめ

複数の Google カレンダーを自動で同期させる【Google Apps Script】

開発の動機

仕事の都合で、本社と出向先の複数の Google カレンダーを使っている同僚がいて、
どちらかのカレンダーに入った予定をもう片方のカレンダーにも入れていないと、
無理な時間に予定を入れられてしまうという問題がありました。

Google Apps Scriptで複数のGoogle Calendarを同期する|Shota Soetani (Shawn)』の記事によって
その問題は解決されていたのですが、あるとき、正常に動かなくなってしまいました。

見せてもらったところ、原因は仕事用の Google カレンダーだと使う「勤務場所」や「不在」の設定にありました。

Calendar#getEvents関数 では、これらのイベントも拾うのですが、ゲストの追加は出来ないイベントのため、
これに対して CalendarEvent#addGuest関数 を呼ぶとエラーになってしまうようです。

「勤務場所」の設定
「不在」の設定

残念ながら、Google Apps Script 向けに用意されている Calendar Service でこの問題を解決することは、
今のところはメソッドが足りずに難しそうでしたので、新たにコードを書くことにしました!

設定方法(使い方)

というわけで、ここからは使い方の説明です。
参考元の記事と同様に、 同期する Google カレンダーそれぞれのアカウントで設定をおこなう必要 があります。

以下では、「アカウントA」「アカウントB」2つのカレンダーを同期させたい、
という場合を想定して解説していきます。

設定が完了すると、アカウントAの予定にアカウントBがゲストとして呼ばれ、
アカウントBの予定にアカウントAがゲストとして呼ばれるようになることで、
どちらのカレンダーにも同じスケジュールが自動で入ってくる状態になります。

1: アカウントAでの設定

まずはアカウントAでの設定です。

1-1: Google Apps Script のプロジェクトを新規作成

https://script.google.com/home を開いてください。

右上のアイコンがアカウントAのものかを確認 し、
違うのであれば目的のアカウントにログインしてください。

問題なければ、「+ 新しいプロジェクト」ボタン からプロジェクトを新規作成します。

1-2: コードをコピペして貼り付け

エディタが開かれたら、すでに書かれている「myFunction」的なコードは丸々削除して、
こちらのページ に記載されているコードをそのまま貼り付けてください。

こんな画面になったと思います

1-3: コードを少しだけ変更

一番上の

const GUEST_EMAILS = [ // 招待するゲストのメールアドレス
  'xxx@example.com',
];
const FETCHING_DAYS = 30; // 何日後までの予定を取得するか

の部分を変更します。

xxx@example.com 」のところは、
同期したい アカウントBのメールアドレス に置き換えます。

同期したいアカウントがもっとある場合は、

const GUEST_EMAILS = [ // 招待するゲストのメールアドレス
  'b-san@example.com',
  'c-sama@example.com',
  'd-kabushiki-gaisha@example.com',
];

のように複数のメールアドレスを書くことも可能です。

もうひとつ、

const FETCHING_DAYS = 30; // 何日後までの予定を取得するか

のところの数字も、必要であれば書き換えてください。
(自分は2ヶ月先まで同期したいので 30 でなく 60 にしています)

ただし、数字が大きいほど実行時間も長くなり、
場合によっては実行が完了せず途中終了するかもしれません。ご注意ください。

1-4: サービスを追加

このままだと実はコードが動きません。

左のメニューにある 「サービス」の「+(プラス)」ボタン を押してください。

その中から「Google Calendar API」を探し出して、「追加」してください。
IDの項目は必ず「Calendar」のままにしておいてください。

Google Calendar API」を追加

これで、コードが動くようになります。

1-5: プロジェクト名の変更

「無題のプロジェクト」ってタイトルだとわかりにくそうなので、
タイトルの部分をクリックして、別の名前をつけてあげましょう。

今回は「Sync Calendar to B-san」としておきました。

1-6: トリガーの設定

左のメニューにある 時計マークのアイコンの「トリガー」 の設定をします。

「トリガー」の設定画面に進んだら、 「+ トリガーを追加」ボタン からトリガーを追加します。

トリガーを追加する

トリガーの設定例

トリガーは、上の画像のように実行する関数が「main」になっているかを特に注意してください。

1日1回の同期があれば充分なので私は「日付ベースのタイマー」を選んでいますが、
予定の入る頻度が頻繁な方は、「分ベースのタイマー」や「時間ベースのタイマー」を選ぶのもよいでしょう。

保存ボタンを押すと、アカウント選択画面が出るのでアカウントAのものを選びます。

人によっては警告画面が出ることがありますが、
特に問題ないはずですので「Advanced」を押して進んでください。

「Advanced」を押す

「Go to (プロジェクト名)」を押す

Google カレンダーへのアクセスをしていいのかの確認が出ますので、「許可(Allow)」をクリックしてください。

これにて、アカウントAでの設定が完了です!!
アカウントAの予定すべてにアカウントBが招待されるようになりました。

2: アカウントBでの設定

アカウントBでやることも同じです。
同じですので、いくらか省略しつつ書いていきます。

2-1: Google Apps Script のプロジェクトを新規作成

https://script.google.com/home を開いてください。

右上のアイコンがアカウントBのものかを確認 し、
(アカウントAのままでないかご注意を!)
違うのであれば目的のアカウントにログインしてください。

問題なければ、「+ 新しいプロジェクト」ボタンからプロジェクトを新規作成します。

2-2: コードをコピペして貼り付け

エディタが開かれたら、すでに書かれている「myFunction」的なコードは丸々削除して、
こちらのページ に記載されているコードをそのまま貼り付けてください。

2-3: コードを少しだけ変更

一番上の

const GUEST_EMAILS = [ // 招待するゲストのメールアドレス
  'xxx@example.com',
];
const FETCHING_DAYS = 30; // 何日後までの予定を取得するか

の部分を変更します。

xxx@example.com 」のところは、
同期したい アカウントAのメールアドレス に置き換えます。

const FETCHING_DAYS = 30; // 何日後までの予定を取得するか

も必要に応じて書き換えてください。

2-4: サービスを追加

左のメニューにある 「サービス」の「+(プラス)」ボタン を押し、
Google Calendar API」を探し出して、「追加」してください。
IDの項目は必ず「Calendar」のままにしておいてください。

これでコードが動く状態になります。

2-5: プロジェクト名の変更

「無題のプロジェクト」ってタイトルだとわかりにくそうなので、こちらも変更します。
今回は「Sync Calendar to A-san」としておきました。

2-6: トリガーの設定

1-6 を参考に、同様のトリガーを設定します。

トリガーの保存時にアカウント選択画面が出ると思うので、
そこではアカウントBのものを選ぶようにしてください。

トリガーの保存がし終わったら、アカウントB側の設定も完了です!!

おわりに

以上で設定は完了です!
トリガーで設定した時間ごとに、同期処理が走るようになりました!

「トリガーの実行時間まで待てないよ!」という方は、
エディタの「実行」ボタンからすぐに実行することも可能です。

「main」を「実行」する

みなさまの働きやすさがアップしましたら幸いです!

Vultr で Kubernetes ノード立ててみたのでメモ

Kubernetes 初心者として、まずはカンタンに始めようと思って、
Vultr でマネージドな Kubernetes を始めてみることにしたのでメモ書き。

1. ノードの立ち上げ

https://my.vultr.com/kubernetes 内の「Add Cluster」からノードを作成します。

Cluster Name はとりあえず適当な感じで

Cluster Location は Tokyo で

Vultr のよくないところとして、
デフォルトでは値段高めのインスタンスを作成させようとする ので、
Node Pool Type を「Optimized Cloud」から 「Regular Cloud Compute」 の方に変更します。

デフォルトだと 135ドル/月 のプランにさせようとしてきます……

この設定で 20ドル/月 のプランにできます

2. Kubernetes ダッシュボードを見られるようにする

右上の Download Configuration から YAML ファイルをダウンロード。

まだ kubectl コマンドが使える状態になっていなかったので、Homebrew を使ってインストールしました。

brew install kubectl

そうしたら kubectl コマンドが動くことをいったん確認。

kubectl --kubeconfig vke-xxxxxxxxxxxxx.yaml get nodes

NAME                       STATUS   ROLES    AGE   VERSION
test-label-2a1617d0261a   Ready    <none>   15m   v1.28.3
test-label-b5a69dff50c2   Ready    <none>   15m   v1.28.3

https://kubernetes.io/ja/docs/tasks/access-application-cluster/web-ui-dashboard/ を参考に、
ダッシュボードの作成をおこないます。

kubectl --kubeconfig vke-xxxxxxxxxxxxx.yaml apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

ダッシュボードの作成が終わったら

kubectl --kubeconfig vke-xxxxxxxxxxxxxxxxxx.yaml proxy

を実行し、 Starting to serve on 127.0.0.1:8001 と表示されるのを待ちます。

そうすると、
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
にアクセスしてログイン画面を見ることができます。

「Kubeconfig」のほうを選択して、
さっきダウンロードした YAML ファイルを指定して「サインイン」できるかと思いきや、できませんでした。

いろいろ調べた挙げ句、
https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.mdを参考に
以下のような YAML ファイルを dashboard-admin-user.yml として作成しました。

# Creating a Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
---
# Creating a ClusterRoleBinding (管理者権限を付与)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
---
apiVersion: v1
kind: Secret
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
  annotations:
    kubernetes.io/service-account.name: "admin-user"
type: kubernetes.io/service-account-token

その後、

kubectl --kubeconfig vke-xxxxxxxxxxxxx.yaml -f dashboard-admin-user.yaml apply

で適用し、強い権限を持った admin-user を作成。その後、

kubectl --kubeconfig vke-xxxxxxx.yaml get secret admin-user -n kubernetes-dashboard -o jsonpath={".data.token"} | base64 -d

トークンを作成し、そのトークンを入力して「サインイン」したら
無事にダッシュボードに入れました。

最初、上のように「Cron ジョブ」や「イングレス」の項を
権限不足で読み込めない症状があって困りました。

これは

kubectl --kubeconfig vke-xxxxxxxxxxxxx.yaml apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

v2.7.0 のところを、ドキュメント通りに古い v2.0.0 を指定していたためでした。

最新版のダッシュボードだと問題が起こりませんでした! よかった。

3. Pod の立ち上げ

右上の「+(プラス)」ボタンから Pod の作成ができます。

https://kubernetes.io/docs/tasks/run-application/run-stateless-application-deployment/ に書かれている
YAML ファイルの内容を記載し、nginx の Pod を立ち上げてみました。

上のように記載し「アップロード」ボタンを押すと……

このようにデプロイされました。

これでいったん Kubernetes に入門できた気がします。

anyenv + n から asdf に乗り換えた感想

anyenvn を使っていたのですが、
PCを新調したのを機に、 asdf に乗り換えてみました。

1. anyenv で抱えていた問題点

rbenvpyenv などを一括で管理できる anyenv ですが、
当然、 rbenv などが抱えている問題を持っています。

それは、インストールできる Ruby バージョンの更新がちょい面倒なところです。

cd ~/.anyenv/envs/rbenv/plugins/ruby-build
git clone

をおこなわないと、 rbenv install --list で表示される
インストールできる Ruby のバージョンが更新されません。pyenv も同様です。

2. n で抱えていた問題点

n はとっても便利で、
n lts で Node.js の最新の LTS バージョンを入れてくるのがとても良いです。

一方、 .node-version ファイルがディレクトリにあっても自動で Node.js のバージョンを切り替えてくれる機能はありません。
n auto をおこなわないといけません。

また、インストールがちょっと面倒で、
昔は npm i -g n だけで完了していたのですが、
Mac/usr/local ディレクトリの権限を厳しくした関係で、今は README にあるように

# make cache folder (if missing) and take ownership
sudo mkdir -p /usr/local/n
sudo chown -R $(whoami) /usr/local/n
# make sure the required folders exist (safe to execute even if they already exist)
sudo mkdir -p /usr/local/bin /usr/local/lib /usr/local/include /usr/local/share
# take ownership of Node.js install destination folders
sudo chown -R $(whoami) /usr/local/bin /usr/local/lib /usr/local/include /usr/local/share

をおこなう必要があります。なかなか面倒です。
Linux だとこのインストールの問題はないのですけどね。

3. asdf を試しに使ってみた

上で書いた話を解決できるのかはわからないけれど、
周りで使っている人も多いので試しに使ってみました。

3-1. インストール

公式ドキュメントだとインストールの方法はけっこう複雑なのですが、
Mac だと brew install asdf だけで済みます。

終わったら、 ~/.zshrc 内の anyenv の設定はコメントアウトし、asdf の設定を記述します。

# anyenv
- if which anyenv > /dev/null; then eval "$(anyenv init -)"; fi
+ # if which anyenv > /dev/null; then eval "$(anyenv init -)"; fi
+
+ # asdf
+ source /opt/homebrew/opt/asdf/libexec/asdf.sh

asdf plugin list allプラグイン一覧を確認したあと、

asdf plugin add ruby
asdf plugin add nodejs
asdf plugin add python

のようにプラグインを入れていきました。
asdf plugin add ruby nodejs python って1行で済むようになったらいいなあ…… )

3-2. 設定の更新

asdf デフォルトの .tool-versions でなく
.ruby-version などを見たり作成したりするようにしたいので、
~/.asdfrc ファイルを作成し、以下の設定を書きました。

legacy_version_file = yes

(参考: https://github.com/asdf-vm/asdf-ruby#migrating-from-another-ruby-version-manager

3-3. グローバルへのインストール

最新バージョンのインストールをそれぞれおこない、グローバルで使うよう設定していきます。

asdf install ruby latest
asdf install python latest
asdf install nodejs $(asdf nodejs resolve lts --latest-available)

本来は Node.js の LTS は asdf install nodejs lts でインストールできるのですが、
現在こちらの issue にある通り正しく動かないので、
仕方ないので asdf install nodejs $(asdf nodejs resolve lts --latest-available) でどうにかしました。

そしてデフォルトのバージョンをこの最新版に設定しました。

asdf global ruby latest
asdf global python latest
asdf global nodejs $(asdf nodejs resolve lts --latest-available)

3-4. プロジェクトで使うバージョンのインストール

.ruby-version のあるフォルダで asdf current をおこなうことで、
必要なインストールコマンドがわかります。

asdf current

nodejs          20.9.0          /Users/nekonenene/.tool-versions
python          3.12.0          /Users/nekonenene/.tool-versions
ruby            3.1.0           Not installed. Run "asdf install ruby 3.1.0"

このコマンドを実行すると、そのフォルダで自動的に Ruby のバージョンが切り替わるようになります。

4. 抱えていた課題は解決できたか

ざっと asdf を使ってみました。

1. と 2. の項で触れた問題点は解消できたのでしょうか?

  • インストールできる Ruby バージョンの更新がちょい面倒な問題
    • インストールできるバージョンの更新は、 asdf list all python のタイミングで自動で完了しますcd コマンドでの移動など不要になりました。便利ですね!
  • .node-version ファイルがディレクトリにあっても自動で Node.js のバージョンが切り替わらない問題
    • asdf では .node-version.ruby-versionディレクトリにあれば、自動でバージョンが切り替わります
    • ただし、 n auto と違って package.json 内に書いてある Node.js バージョンは読み取りません

このように、今までの課題は解決されました!!

5. asdf のデメリット

一方でデメリットもいくつかあります。

  • コマンドが少し長くてわかりにくい。慣れかもしれませんが、今のところ asdf コマンドを打ってコマンドを確認することが多いです。 asdf plugin などと打ったときにもヘルプの一部が出てきたらうれしいなあ、と思います
    • リポジトリを見たらプルリクが出ていますね。マージされるといいなぁ
    • ただ、現在 asdf のメンテナーは jthegedus さんほぼ1人の状態で回っているので、マージまでが難しいかもしれません
  • 3-3. の項で書いたバグの問題で、Node.js の LTS バージョンを現在はカンタンにおこなえないのもマイナスです。 asdf install nodejs lts が機能するよう直るといいですね……!

上のデメリット2つはコントリビュートで直せる可能性があるので、
(コミッターの人たちが忙しそうでマージまでが難しいかもしれませんが)
そこも念頭に入れつつ、今後は asdf を使ってみようかなと思いました。