Slide 1

Slide 1 text

TUI の Feed Viewer を自宅の Raspbery Pi で 公開するまで Nix で Rust の開発環境から CI,Deploy まで管理する 山口 裕太 · 2024‑03‑05

Slide 2

Slide 2 text

自己紹介 [speaker] name = "Yamaguchi Yuta" github = "https://github.com/ymgyt" blog = "https://blog.ymgyt.io/"

Slide 3

Slide 3 text

会社紹介 [company] name = "FRAIM Inc." tech_blog = "https://zenn.dev/p/fraim"

Slide 4

Slide 4 text

話すこと 作成したツールのライフサイクルについて • TUI の Feed Viewer を作った • Nix で開発環境,CI,Deploy を管理した • Release では cargo‑release と cargo‑dist を利 用した • Trace,Metrics,Logs は OpenTelemetry を利用し た

Slide 5

Slide 5 text

Rust で TUI Feed の Entry 一覧

Slide 6

Slide 6 text

Rust で TUI Feed の一覧

Slide 7

Slide 7 text

Rust の TUI library 1. crossterm(cross platform terminal library) 2. ratatui(tui widgets library)

Slide 8

Slide 8 text

Rust の TUI library Netflix が公開した eBPF の tui tool でも ratatui が使われている

Slide 9

Slide 9 text

ratatui let (header, widths, rows) = self.entry_rows(cx); let entries = Table::new(rows, widths) .header(header.style(cx.theme.entries.header)) .column_spacing(2) .style(cx.theme.entries.background) .highlight_symbol(ui::TABLE_HIGHLIGHT_SYMBOL) .highlight_style(cx.theme.entries.selected_entry) .highlight_spacing(HighlightSpacing::WhenSelected); Table や List 等の基本的な widget が用意されている

Slide 10

Slide 10 text

crossterm async fn event_loop(&mut self, input: &mut S) where S: Stream> + Unpin, { loop { tokio::select! { biased; Some(event) = input.next() => { } _ = self.background_jobs => { } }; } 複数の入力も async で書ける

Slide 11

Slide 11 text

crossterm fn handle_terminal_event(&mut self, event: std::io::Result) { match event.unwrap() { CrosstermEvent::Resize(columns, rows) => { } CrosstermEvent::Key(key) => match key.code { KeyCode::Tab => { }, KeyCode::Char('j') => { }, } } } Key 入力の処理も簡単

Slide 12

Slide 12 text

Nix による開発 環境の管理 この Rust の project を Nix で管理しました

Slide 13

Slide 13 text

Nix による開発 環境の管理 そもそも Nix とは > A build tool, package manager, and programming language https://zero‑to‑nix.com/concepts/nix

Slide 14

Slide 14 text

Nix で管理する と何がうれし いのか • 開発環境を Nix で管理できる

Slide 15

Slide 15 text

Nix による開発 環境の管理 { inputs.nixpkgs.url = "github:NixOS/nixpkgs/ release-23.11"; outputs = { self, nixpkgs, ... }: let dev_packages = with pkgs; [ graphql-client git-cliff cargo-release cargo-dist oranda ]; in { devShells.default = craneLib.devShell { packages = dev_packages; }; }); } project root の flake.nix で開発時の依存を宣言

Slide 16

Slide 16 text

nix develop nix develop を実行すると宣言した package が PATH に入った状態で開発環境(shell)が立ち上が る

Slide 17

Slide 17 text

Nix で管理する と何がうれし いのか • 開発環境を Nix で管理できる • CI を Nix で管理できる

Slide 18

Slide 18 text

CI でも nix develop name: CI jobs: tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: nix develop .#ci --accept- flake-config --command just check • install action が不要 • 開発環境と同一 version

Slide 19

Slide 19 text

Nix で管理する と何がうれし いのか • 開発環境を Nix で管理できる • CI を Nix で管理できる • Cache を Nix で管理できる

Slide 20

Slide 20 text

Nix で管理する と何がうれし いのか /nix/store/3sn5pij1hjvdlnq468c0m8vz8nibhrdc- cargo-release-0.25.0/bin/cargo-release の 3sn5pij1hjvdlnq468c0m8vz8nibhrdc が入力に対 応する hash となっており、cache があれば build せずに binary を取得できる

Slide 21

Slide 21 text

Nix で管理する と何がうれし いのか cache から取得

Slide 22

Slide 22 text

Nix で管理する と何がうれし いのか name: CI jobs: tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v25 with: github_access_token: $ {{ secrets.GITHUB_TOKEN }} - uses: cachix/cachix-action@v14 with: name: syndicationd authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - run: nix develop .#ci --accept-flake-config -- command just check cache を設定した最終的な CI の workflow local と CI で同じ cache の仕組みを利用できる

Slide 23

Slide 23 text

Nix で管理する と何がうれし いのか • 開発環境を Nix で管理できる • CI を Nix で管理できる • Cache を Nix で管理できる • Install を Nix で管理できる

Slide 24

Slide 24 text

Nix で管理する と何がうれし いのか nix run で一時的に実行したり nix profile で install もできる Mac と Linux で同じ package 管理ができる

Slide 25

Slide 25 text

Nix で管理する と何がうれし いのか • 開発環境を Nix で管理できる • CI を Nix で管理できる • Cache を Nix で管理できる • Install を Nix で管理できる • Deploy を Nix で管理できる

Slide 26

Slide 26 text

Nix で systemd service を宣言 { config, pkgs, syndicationd, ... }: let syndPkg = syndicationd.packages."${pkgs.system}".synd- api; in { config = { systemd.services.synd-api = { description = "Syndicationd api"; wantedBy = [ "multi-user.target" ]; environment = { SYND_LOG = "INFO"; }; serviceConfig = { ExecStart = "${syndPkg}/bin/synd-api" }; }; }; } https://github.com/ymgyt/mynix/blob/main/homeserver/ modules/syndicationd/default.nix

Slide 27

Slide 27 text

Nix で Deploy deploy‑rs で NixOS が入った RaspberryPi に deploy

Slide 28

Slide 28 text

Nix で管理する と何がうれし いのか • 開発環境を Nix で管理できる • CI を Nix で管理できる • Cache を Nix で管理できる • Install を Nix で管理できる • Deploy を Nix で管理できる

Slide 29

Slide 29 text

cargo でも公開 したい とはいえ、cargo install もしたい

Slide 30

Slide 30 text

cargo でも公開 したい cargo publish するには以下が必要 • Cargo.toml の package.version の bump • Workspace 内に依存 package がある場合は合 わせて bump • CHANGELOG の生成 • git の tagging これを workspace の member crate ごとに実施 する必要がある

Slide 31

Slide 31 text

cargo でも公開 したい cargo‑release が全て面倒をみてくれる

Slide 32

Slide 32 text

cargo でも公開 したい cargo release --package foo patch を実行する と • foo package の Cargo.toml package.version を bump(v0.1.2 ‑> v0.1.3) • foo に依存している workspace 内の package の dependencies.foo.version を bump • 設定に定義した replace 処理を実施 (CHANGELOG の[unreleased] ‑> [v0.1.3]) • git の commit, tagging, push • cargo publish

Slide 33

Slide 33 text

homebrew で も公開したい brew install もしたい

Slide 34

Slide 34 text

homebrew で も公開したい 専用の repository(homebrew-syndicationd)に以下 のような記述が必要 class Synd < Formula desc "terminal feed viewer" version "0.1.6" on_macos do on_arm do url "https://github.com/ymgyt/syndicationd/releases/download/synd- term-v0.1.6/synd-term-aarch64-apple-darwin.tar.gz" sha256 "c02751ba979720a2a24be09e82d6647a6283f28290bbe9c2fb79c03cb6dbb979" end on_intel do url "https://github.com/ymgyt/syndicationd/releases/download/synd- term-v0.1.6/synd-term-x86_64-apple-darwin.tar.gz" sha256 "3be81f95c68bde17ead0972df258c1094b28fd3d2a96c1a05261dba2326f31d8" end end

Slide 35

Slide 35 text

homebrew で も公開したい cargo‑dist が全て面倒をみてくれる

Slide 36

Slide 36 text

cargo‑dist が やってくれる cargo dist init を実行して質問に答えると • Cargo.toml の[workspace.metadata.dist]に設 定が追加 • .github/workflows/release.yml を生成

Slide 37

Slide 37 text

cargo‑dist が やってくれる cargo dist init を実行して質問に答えると • Cargo.toml の[workspace.metadata.dist]に設 定が追加 • .github/workflows/release.yml を生成 この状態で cargo‑release で git tag を push する と

Slide 38

Slide 38 text

cargo‑dist が やってくれる Github release と各種 installer が作成される

Slide 39

Slide 39 text

cargo‑dist が やってくれる cargo‑dist が以下をやってくれる(設定次第) • 各種 platform(aarch64,x86 の darwin,windows, linux gnu,musl)向けの binary 生成 • shell,powershell の install script 作成 • homebrew 用 repository の更新(push 権限を渡 す必要あり)

Slide 40

Slide 40 text

cargo‑dist が やってくれる cargo dist plan を実行すると CI で実行される 内容を確認できる workflow yaml にベタ書きされているのではな く、cargo dist plan の出力を参照するように なっている

Slide 41

Slide 41 text

cargo‑dist が やってくれる cargo‑release と一緒に使うことが想定されてお り cargo publish と git の tag push までが cargo‑ release それ以降の配布処理が cargo‑dist という役割分 担がよかったので使ってみた

Slide 42

Slide 42 text

監視もしたい Deploy と Release ができたので次に監視がした い

Slide 43

Slide 43 text

監視もしたい Deploy と Release ができたので次に監視がした い OpenTelemetry を使う

Slide 44

Slide 44 text

Rust で OpenTelemetry Rust で tracing を使っている場合 tracing-opentelemetry と opentelemetry_sdk を 使うと Logs, Traces, Metrics を取得できる

Slide 45

Slide 45 text

Rust で OpenTelemetry 公開している Grafana dashboard

Slide 46

Slide 46 text

Rust で OpenTelemetry Entry 取得時の Trace

Slide 47

Slide 47 text

まとめ Nix,cargo‑{release,dist},OpenTelemetry で 楽しいツール開発

Slide 48

Slide 48 text

各種 link • cargo‑release • cargo‑dist • deploy‑rs • syndicationd • raspi nix configuration