TL;DR Context: internalスコープのSwiftソース定義を一括でpublic化したい場合(モジュール切出しなどの文脈) Problem: 正規表現によるコード変換では対応しきるのは難しい Solution: SwiftSyntaxのSyntaxRewriterをつかうことで、セマンティックなコード変換を比較的容易に実装できる Result: https://github.com/hrfmmr/swift-public-rewriter Context Swiftプロジェクトで、モデル類のソースを別のモジュールに切り出すといった作業が嵩むことがあり、効率的に行いたいというモチベーションがあった
単体のソースとしてはいずれも以下のようなもの
struct Foo { let x: Int let bar: Bar init(x: Int, bar: Bar) { self.x = x self.bar = bar } func doSomething() { let a = ... var b = ... } } extension Foo { struct InnerFoo {} } enum Bar { case x var hoge: Int { switch self { case .x: ... } } } class Baz { private(set) var x: Int = .
Read more
nginxのトレース情報を収集できるDatadog nginxモジュールのv1.0.0が最近リリースされていたので試した
ref. https://docs.datadoghq.com/tracing/trace_collection/proxy_setup/?tab=nginx#nginx-with-datadog-module
docker-compose.yml
version: "3.7" services: api: build: context: ./api dockerfile: Dockerfile args: DD_API_KEY: ${DD_API_KEY} container_name: sample-api ports: - 8080:8080 environment: - DD_ENV=dev - DD_HOSTNAME=local nginx: build: context: ./nginx dockerfile: Dockerfile container_name: sample-nginx image: sample-nginx labels: com.datadoghq.tags.env: 'dev' com.datadoghq.tags.service: 'sample-nginx' com.datadoghq.tags.version: '0.1.0' com.datadoghq.ad.check_names: '["nginx"]' com.datadoghq.ad.init_configs: '[{}]' com.datadoghq.ad.instances: '[{"nginx_status_url": "http://%%host%%:81/nginx_status/"}]' com.datadoghq.ad.logs: '[{"source": "nginx", "service": "sample-nginx"}]' volumes: - './nginx/nginx.conf:/etc/nginx/nginx.conf:ro' ports: - "8888:80" environment: DD_AGENT_HOST: datadog DD_TRACE_AGENT_PORT: 8126 datadog: image: datadog/agent:7 container_name: sample-ddagent environment: - DD_API_KEY - DD_SITE=datadoghq.
Read more
という巷によくありそうなモノをつくった。
kidsを保育園に預けていて、定期的に園からのおたよりで「mm/ddまでにxxxをもってきてほしい/つくってきてほしい」といった要求が届く。「歯磨きするから歯ブラシをビニールケースに入れて」「箸を練習していきます。箸を箸袋に入れてそれを昼食袋に」「プール開きに伴いxxx, yyy, zzzを入れたプールバッグを斯々然々..」といったもの。
弊家は家庭ナレッジをすべてScrapboxに集約していて、園からのおたよりもまた例によってScrapboxにアーカイブされている。というところで、記録は問題なくできている一方で、記憶がトぶことがしばしばあり、dueまでに指定のモノを用意できない/持参し忘れるということがしばしばあった。
カレンダに予定を入れておいてもなにかと忘れ、都度Slackのリマインダ通知を設定するのも作業的に面倒くさくてやってられない…と業を煮やした末、「対応を忘れないようにするための通知」を自動化しようとおもった。 (タスク管理といえばタスク管理だから専用のツールで管理という選択肢はあるのだけれども、家庭で扱うツールはなるたけ増やしたくないという事情もあった。ツール増やした先でコメント機能なんかがあるとコンテキストやナレッジが分散しがちでそれは避けたいなど)
それ系のGoogleカレンダ+Slackなインテグレーションはおあつらえのものがきっとあるとおもって探したが、ほしかったものはなさそうだったので自前でつくることにした。
つくったもの AWS Lambda+EventBridgeベースのスケジューラ EventBridgeでLambdaを定期実行するスケジューラを定義 LambdaはGoogleCalendar APIを叩いて向こう1週間分の指定カレンダの予定を取得 1日後/3日後/1週間後にあたるイベントを選別し、指定Slack channelに通知 Slack通知サンプル Slackアプリのアイコン画像は以下のような呪文でStable Diffusionで生成したらかわいいのが出来た。生成AI便利。 Cute robot holding a calendar in its hand and reading it out in Monet style Lambda(Pythonランタイム)本体のコードとしては以下のようなもの
import datetime import json import logging import os import re from collections import defaultdict from dataclasses import dataclass from enum import Enum from typing import Optional import boto3 import slack_sdk import slack_sdk.
Read more
Context
AWSコンテナ設計・構築入門を読んだ続きとして、 ECSリソースについては他AWSリソースとライフサイクルが異なるとして、ECSはTerraformではなくecspressoによる構成管理を行うとよさそう モチベーション
TerraformでECSリソースを管理しようとすると、以下のような課題がある
a.) TerraformでECSタスク定義を管理する上での課題
aws_ecs_task_definitionを更新すると、タスクリビジョンの積み上げではなく差替(replacement)の差分が生じる リビジョンを戻すということができない rollback相当のことをしたい場合、前回のタスク定義でまたreplacementする差分をapplyする 課題 リビジョンを遡ることができない 過去のリビジョンを(gitから)探し出すのが手間 迅速なrollbackができない rollbackは思考を自動化した手順で行いたいが、それを妨げる b.) TerraformでECSサービスを管理する上での課題
Auto Scalingによってdesired_countの変更が起き得る aws_ecs_serviceで定義しているdesired_countは、たとえばaws_appautoscaling_target定義によるAuto Scaling発動によって、terraform applyを介さずに状態が変わり得る -> Terraform定義と稼働しているECSサービスとの間に差分が生まれる 課題 定義と実態で差分がある状態でterraform applyが実行されると(定義自体に差分は生じず)、Auto Scalingによるスケールアウトを意図せず打ち消してしまうなどが起きる ignore_changesに含めて意図しない「修正」を行わないようにすることは可能だが、本来のdesired_countを変更したいときなどはignore_changes外す必要があり、トリッキーな対応を要する いずれにせよ、applyが意図した収斂になるか判別できない(tfstateが最新状態を反映しているかが不明である以上は) ecspressoによるECS管理
ecspressoは何を解決するか a.) Terraform ECSタスク定義のリビジョンが残らない/rollback問題 タスク定義の登録(register)や実行(run)が個別で行える ECSサービスのデプロイ(deploy)で、“最新リビジョンのタスク定義を参照(新規登録しない)“オプション(--latest-task-definition)の付与が可能 -> タスク定義登録前のリビジョンへのロールバックが可能(rollback ) b.
Read more
AWSコンテナ設計・構築[本格]入門 を読んだ
コンテナオーケストレータとしてはKubernetesは馴染みがあったがECSはなにもわからない勢だったので、わかりたいモチベーションで本書を手にとった Terraformも何気に触れてこなかったので、その学習も兼ねて本書で扱うシステム構成をTerraformで構築していってみた Terraform化で扱った構成図(Well-Architectedにはしきれていない..) ※以下は単位AZの図(構築するものはマルチAZ構成) github repo: https://github.com/hrfmmr/sbcntr-resources/tree/terraform/terraform
書籍と異なる部分
bastion パブリックサブネットに踏み台サーバを置いている 書籍ではVPC内で行う必要のある作業(Internal ALBへの疎通確認やDB seedingなど)は、Cloud9環境で行う内容となっていた が、個人的には再現性犠牲にしてもローカルマシンから環境構築作業を行いたかったので、VPC内で行う作業用に踏み台EC2を建てる構成にした network 書籍ではコンテナからのアウトバウンド通信はNATゲートウェイを介さずすべてVPCエンドポイントで解決していた が、NATを置く構成にしてる デプロイしたコンテナが意図通りに動作しないことがあった(frontend appからbackend appへの通信が通らないなど)ので、コンテナの中に入って調査したいことがあった その過程でdns-utilsなどのパッケージインストールを行えるようにしたかったという理由(slimイメージだと何も入っていない) 運用readyになったら不要になるやつ ecs タスクロールにECS Exec用のロールを追加してる 上述の「(Fargate)コンテナに入って調査」するためにECS Exec用のポリシーをアタッチしている secrets 書籍ではAWS Secrets ManagerでRDS DBのcredentialsを設定していたが、AWS Systems Managerのパラメータストアで代用している Secrets Managerを使う一方でシークレットの自動ローテーションをOFFにしていたので、なぜパラメータストアではなくSecrets Managerを選択しているのか気になった AWSの公式FAQ をみると、両者のセキュリティモデルに違いはないとしていて、それぞれのユースケースとしてはライフサイクル管理要件ないならパラメータストア/あるならSecrets Managerをつかうという記載があった rds DBはAmazon Auroraをつかう前提だったが、学習のためとはいえさすがにコスト嵩みそうなのが怖かったので、Aurora構成は断念した 「ECSタスクからRDSにつながる」部分を最小で実現できさえすればひとまず良しとして、シングルAZのDBインスタンスを建てるのみの構成にした 感想戦
Read more
Context Obsidian vaultのback up先として、privateのGitHub repoへ置いている iOSアプリとの連携用でiCloud同期は別でやっている iCloudストレージの操作をミスっておじゃんにすることもあろうかと、git repoへもsyncしておいて安心しておきたい 日々差分が生じているが、push作業は手動でおもいついたらやっている。アドホック運用。(数日だったり1週間空くこともありがち) syncを自動daily実行されるしくみがほしい git-syncをつかうとよさそう git-syncを実行する git-syncをcloneしてPATHを通す
❯ git clone https://github.com/simonthum/git-sync.git ❯ sudo ln -snfv `pwd`/git-sync /usr/local/bin/git-sync Obsidian vaultのあるディレクトリへ移動
❯ cd /path/to/ObsidianVault git-sync用のgit config設定
❯ git config --bool branch.main.sync true ❯ git config --bool branch.main.syncNewFiles true .git/configに以下が追加される
[branch "main"] remote = origin merge = refs/heads/main + sync = true + syncNewFiles = true git-sync実行
❯ git-sync git-sync: Preparing. Repo in .
Read more
というのがこの頃個人的最適解となっているのでそれについて整理してみた
TL;DR Obsidianつかうとタスク管理とナレッジ管理を圧倒的操作性の下で完結できてうれしい
前提 タスク管理について述べていく前にまず前提として「タスク」が指すものについての補足 以下で述べられる「タスク」は基本的にソフトウェアエンジニアリング的なことを行う過程で発生する「技術的なタスク(実装事/リサーチ事他)」を想定している 「タスク管理」のスコープとするところ 主体は 個人に閉じた話(チームで何らか共同でタスク管理していく話とは別) スコープ 「なにをせねばならんか」「いますべきことは」「やるなかで得られた知見の蓄積」をいかに効率的に行うか、が主眼 「いついつまでに間に合いそうか」といった進捗管理的なことは関心の対象外 タスク管理の基本要素 タスク管理どうやってますかを語る上で、避けて通れない系の基本要素を整理してみると、おおまか以下がありそう
a.) (タスクを)一覧する b.) (タスクを)選択する c.) (タスクを)開始/再開する d.) (タスクを)実行する タスク管理における基本要素毎の課題感 a.) ~ d.)の4項目について、それぞれの課題感を書き綴ってみると
a.) (タスクを)一覧する 課題感 「TODOリスト」のつらさ わすれないようにするために人は「TODOリスト」をつくる いずれ長くなってくる リストと向き合うたびに認知負荷がかかる 本来必要なのは「いま集中して取り組むやつ」だけ それ以外のも目に入ってくる。ノイズ。 「あれやんなきゃ」とおもいついたら「リスト」に移す 「リスト化」自体がコスト 「リスト」へのコンテキストスイッチが起きる 入力が面倒 複数コンテキスト(ie. プロジェクト)のTODOアイテムを扱う必要が生じた場合どうするか コンテキスト境界でTODOリスト自体分ける? 複数のTODOリストを管理する TODOリストを行き来する必要が生じる。管理コストがつらくなる。 1枚のTODOリストで複数コンテキストを区別する?
Read more
Context iOSアプリ UIKitコードベースとの付き合い feature-modularizedなマルチモジュール構成をとった場合の課題 課題 Featureモジュール間の画面遷移をどう実現するか why: featureモジュール同士は依存関係をつくれない(循環参照) so: FeatureAからFeatureBの画面(VC)インスタンスを生成したい場合、globalに定義される共通の抽象インタフェースへの依存で解決する必要性が生じる VC生成にあたり、それにぶら下がる依存解決上の課題 ときに、single source of truth用途(インメモリで保持される状態アクセスなど)のsingletonインスタンスをFeatureモジュール間で引き回したい。暗黙依存でなくDI(constructor injection)の形式で。テスト容易性。 得てして、このインスタンスが実際に用いられる層はVCではなく、依存先の層(model/usecase)であるケースがほとんど 依存解決のためのインスタンスのバケツリレー 使用側(下位)のコンストラクタ(DI仕様)変更に伴う、上位側からの依存注入し直しバケツリレー変更のつらみ 依存解決やり方 モジュール内に存在する各VCを生成するためのFactoryなインタフェースをglobalに定義(eg. 各Featureモジュールが共通に依存するCoreモジュールに定義するなど) 各Featureは共通化されたFactoryインタフェースを用いて別Featureの画面を生成し、遷移先として指定する 各社の知見 cookpad社の取り組み ViewBuilderがFeature内VC生成のインタフェース Coreに定義されるEnvironmentをつかってbuild buildの中でRouter初期化時にenvironmentをDI Router内で任意のViewDescriptor(これはCoreレベルで公開)を初期化、environment.resolve(descriptor)でVCインスタンスを得る application targetで、Environment protocol準拠することで、ViewDescriptor毎のVCインスタンス生成の具象コードを実装 課題 Environmentのresolve実装するとき、Descriptorの網羅が必要 Descriptorはstaticなenumではないので、case網羅のためにコンパイラチェックの恩恵を受けられない switch caseを網羅するのに工夫が必要(実際cookpadでは存在するDescriptorのcase網羅が行われるようにコードの自動生成のしくみを構築していて、カバーされていないcaseがあればコンパイルエラーになるようにしているとのこと) しくみの構築と運用のコスト もっとラクにコンパイルタイムでの依存解決を保証するしくみがあるとうれしい はてな社の取り組み uber/needleを用いたDIコードの自動生成(≒ Dagger) 型安全なEnvironment実装生成不要でViewBuilder相当のDI定義を各Featureモジュールに寄せられる 本体app targetではRootComponentとそれにぶら下がる各FeatureComponentが必要とするFactoryを定義するだけで済む 依存解決の不足をコンパイラチェック可能に 実装How uber/needle CoreにVCインスタンス化用のBuildable定義 FeatureでComponent定義 画面に対応するBuildable型を返却するBuilderを内部で実装 app targetでRootComponentを定義 childとなる各FeatureComponentのDependencyに必要なfactory定義を施す singletonを引き回したい場合は、 NeedleFoundationのsharedでfactoryをラップすることで解決できる 図 app target RootComponent Features Feature Alpha FeatureAlphaComponent FeatureAlphaFooBuilder Feature Bravo FeatureBravoComponent FeatureBravoFooBuilder Core AlphaFooBuildable BravoFooBuildable 成果物 https://github.
Read more
Context とあるコードベースでXcodeのビルド時間がインクリメンタルビルドですら3分近くかかる問題がある 「ビルド時間おそいですね」課題感は共通認識なものの、総量として定量的にみてどれくらいの問題の大きさなのかが把握できていない状況でもある ビルド時間を開発速度のhealthcheck的な指標としてモニタリングしたい どうやるか XCMetricsというTHEソレのようなOSSをspotifyが公開していたりする github repo: https://github.com/spotify/XCMetrics (v0系ではあるものの)1st releaseが2021/01とわりと最近 vapor製 Swift大統一 dashboard UIがイケてる XCMetricsはつかえるか🤔 Backend Deploymentをみると、アプリケーション/DBサーバの運用が必要 localhostでつかう分にはDocker imageも提供されているしサクッと動かせる 業務での運用を考えると、サーバをお守りするのはセキュリティ事がついてまわるので話が変わってくる アプリケーションサーバについてはCloud Runにオフロードする構成もとれるっぽいが、別でCloud SQLもとなるとコストの割高感が否めない.. インフラ管理不要なマネージドに寄せつつ低コストでおなじようなことをやりたいというモチベーションがあったりする XCMetricsはビルドログ収集どうやってるの XCLogParserをつかっている とは Xcodeがビルドログとして~/Library/Developer/Xcode/DerivedData/に吐き出す.xcactivitylog(gzip圧縮されたSLF encodedなファイル(?))があって、 これをparseするとビルドの詳細情報(どのschemeで/どのtargetで/どのソースファイルで/どれだけビルド時間がかかったか情報etc)がとれる 解析方法の情報源として@vincentisambart氏によるcookpad社のblog記事がlinkされてる(つよい..) このparse処理をラップしてビルドメトリクス情報をhtmlやjsonで出力できる、というツール なにげにmain contributorがXCMetricsと同じspotifyの人 ビルドログ情報はXCLogParserから取得できるから、以下が揃えばXCMetricsでやってるようなビルド時間可視化はいける ログデータの加工(postBuildScript) ログデータの保存(DB) ログデータの可視化(のfrontend) 「データがあってそれを可視化」といったらBigQuery+Data Portalの構成が降ってくる(というかWebアプリ以外だとそれしかおもいつかない.
Read more
github repo: https://github.com/hrfmmr/swiftui-sandbox
題材 「twsearch」という、Twitter APIのApplication’s only authenticationをつかったシンプルなtweet検索アプリ
モジュール構成 module type description twsearch application 本体アプリ AppFeature dynamic framework 各FeaturesをまとめあげるUmbrella的なFeatureモジュール SearchStatusFeature dynamic framework 検索機能のFeatureモジュール APIClient dynamic framework Twitter APIクライアント SwiftUIHelpers dynamic framework SwiftUIのヘルパ Core dynamic framework ドメインモデル定義など Umbrella static framework 共通で使用されるライブラリ群がembedされたモジュール 依存関係 Embedded Frameworkをつかったモジュール構成 XcodeGen project.yml
Read more