chezmoiでdotfileの管理を楽にする

こんにちは、ナナオです。 今回はchezmoiというツールでdotfileを管理する基盤を作っていこうと思います。 chezmoiとは WSLとかlinuxとかMacとか触ってると、.sshとか.zshrcとかいろいろ出来上がると思います。 chezmoiはそんなdotfileたちを管理するためのツールです。 chezmoi - chezmoi インストール&ファイルを管理対象に含める miseを使ってインストールします。 mise use -g chezmoi brewでもインストールできます。 brew install chezmoi インストールはこれで完了です。 コマンドを確認してみましょう。 ❯ chezmoi Manage your dotfiles across multiple diverse machines, securely Usage: chezmoi [command] Documentation commands: doctor Check your system for potential problems help Print help about a command license Print license Daily commands: add Add an existing file, directory, or symlink to the source state apply Update the destination directory to match the target state chattr Change the attributes of a target in the source state diff Print the diff between the target state and the destination state edit Edit the source state of a target forget Remove a target from the source state init Setup the source directory and update the destination directory to match the target state merge Perform a three-way merge between the destination state, the source state, and the target state merge-all Perform a three-way merge for each modified file re-add Re-add modified files status Show the status of targets update Pull and apply any changes Template commands: cat Print the target contents of a file, script, or symlink data Print the template data execute-template Execute the given template(s) Advanced commands: cd Launch a shell in the source directory edit-config Edit the configuration file edit-config-template Edit the configuration file template generate Generate a file for use with chezmoi git Run git in the source directory ignored Print ignored targets managed List the managed entries in the destination directory unmanaged List the unmanaged files in the destination directory verify Exit with success if the destination state matches the target state, fail otherwise Encryption commands: age Interact with age age-keygen Generate an age identity or convert an age identity to an age recipient decrypt Decrypt file or standard input edit-encrypted Edit an encrypted file encrypt Encrypt file or standard input Remote commands: docker Use your dotfiles in a Docker container ssh SSH to a host and initialize dotfiles Migration commands: archive Generate a tar archive of the target state destroy Permanently delete an entry from the source state, the destination directory, and the state import Import an archive into the source state purge Purge chezmoi's configuration and data upgrade Upgrade chezmoi to the latest released version Internal commands: cat-config Print the configuration file completion Generate shell completion code dump Generate a dump of the target state dump-config Dump the configuration values secret Interact with a secret manager source-path Print the source path of a target state Manipulate the persistent state target-path Print the target path of a source path Flags: --age-recipient string Override age recipient --age-recipient-file string Override age recipient --cache path Set cache directory (default /home/banan/.cache/chezmoi) --color bool|auto Colorize output (default auto) -c, --config path Set config file --config-format <none>|json|toml|yaml Set config file format --debug Include debug information in output -D, --destination path Set destination directory (default /home/banan) -n, --dry-run Do not make any modifications to the destination directory --force Make all changes without prompting -h, --help help for chezmoi --interactive Prompt for all changes -k, --keep-going Keep going as far as possible after an error --less-interactive Prompt for changed or pre-existing targets --mode file|symlink Mode (default file) --no-pager Do not use the pager --no-tty Do not attempt to get a TTY for prompts -o, --output path Write output to path instead of stdout --override-data string Override data --override-data-file path Override data with file --persistent-state path Set persistent state file --progress bool|auto Display progress bars (default auto) -R, --refresh-externals always|auto|never[=always] Refresh external cache (default auto) -S, --source path Set source directory (default /home/banan/.local/share/chezmoi) --source-path Specify targets by source path --use-builtin-age bool|auto Use builtin age (default auto) --use-builtin-diff Use builtin diff --use-builtin-git bool|auto Use builtin git (default auto) -v, --verbose Make output more verbose --version version for chezmoi -W, --working-tree path Set working tree directory Use "chezmoi [command] --help" for more information about a command. なにやらいろいろ出力されました。 ...

2026年1月29日 · にあえん

busyboxベースのNAS(N-RAID 5800M)にtailscale入れてみた

こんにちは、ナナオです。 前回tailscaleを入れてみてかなり良かったので、今回は自宅のNASにも入れていこうと思います。 自宅のNASはbusyboxがベースとなっており、aptなどのパッケージマネージャーがいないので、バイナリインストールをしていこうと思います。 使用しているNASはN-RAID 5800Mになります。 ���m�̔��������� �|NAS RAID�^RAID 6�Ή��^N-RAID 5800M�V���[�Y�s�G�k�E���C�h�t���i�T�v�E���ȓ����| インストール まずはNASのアーキテクチャを確認します。 NASにはSSHでログインしておいてください。 > uname -m x86_64 ということでamd64のバイナリを以下からダウンロードします。 https://pkgs.tailscale.com/stable/#static 以下のコマンドを実行します。 wget --no-check-certificate https://pkgs.tailscale.com/stable/tailscale_1.92.5_amd64.tgz ダウンロードしたアーカイブを解凍します。 > tar xvf tailscale_1.92.5_amd64.tgz tailscale_1.92.5_amd64/ tailscale_1.92.5_amd64/tailscaled tar: tailscale_1.92.5_amd64/tailscaled: Wrote only 3072 of 10240 bytes tailscale_1.92.5_amd64/tailscale tar: tailscale_1.92.5_amd64/tailscale: Cannot write: No space left on device tailscale_1.92.5_amd64/systemd/ tailscale_1.92.5_amd64/systemd/tailscaled.service tailscale_1.92.5_amd64/systemd/tailscaled.defaults tar: tailscale_1.92.5_amd64/systemd/tailscaled.defaults: Cannot write: No space left on device tar: Exiting with failure status due to previous errors ん?なんかエラーが出てますね。。 ...

2026年1月28日 · にあえん

Neonで無料のPostgresを使う

こんにちは、ナナオです。 前回、KoyebでDiscord botを構築してみました。 今回はこれに無料で使えると話題のNeonを組み合わせてみようと思います。 Neonとは Neonはサーバーレスで動くPostgresのサービスです。 Neon Serverless Postgres — Ship faster DBのブランチ戦略やオートスケーリングに長けており、なにより無料で使えるプランがあるのが個人開発者には魅力的な点です。 アカウント登録 ~ プロジェクト作成 まずはNeonにサインアップします。 サインアップしたらプロジェクトの作成画面に遷移します。 プロジェクト名を入力し、リージョンは日本がないので、とりあえずデフォルトのN. Virginiaにしておきます。 プロジェクトが作成されました。 NeonのDBに接続してみます。 Webコンソール上からアクセスしてみましょう。 左側のタブからTablesを選択します。 neondbというDBがあるのと、publicスキーマがあるだけで、テーブルは何もありません。 マイグレーション管理(alembic)の導入 discord botの実装にはpythonを使用しているので、マイグレーションにもpythonを使用します。 まずはプロジェクトにSQLAlchemyとalembicを導入します。 uv add SQLAlchemy alembic psycopg2 alembicでマイグレーション用のディレクトリを作成します。 uv run alembic init migrations 作成されたalembic.iniのデータベースURLの設定を、環境変数を参照するように書き換えます。 # database URL. This is consumed by the user-maintained env.py script only. # other means of configuring database URLs may be customized within the env.py # file. sqlalchemy.url = %(DATABASE_URL)s .envにはDATABASE_URL環境変数を設定します。 ...

2026年1月27日 · にあえん

Zedで最速で最高の開発環境を手に入れる

こんにちは、ナナオです。 今回はRust製のめちゃ早エディター「Zed」を触っていきたいと思います。 初期設定 インストールはwingetから行えます。 winget install -e --id ZedIndustries.Zed インストール後、zedコマンドで起動できます。 起動直後のzedはこんな感じです。 VSCodeとの比較ですが、私個人的にはまぁ早いかなくらいでした。 普段WSLを使うのですが、設定とかは特に必要なくwsl上からzedコマンドが使用できました。 感想 拍子抜けするくらい簡単でした。 今後使っていく中でまた何かあったら追記していこうと思います。 参考 VSCodeをやめてZedにするぞ! - Chienomi ZedのWindows Beta版、WSLでうごくっぽいぞ! #VSCode - Qiita RUST製エディター「Zed」とは #Rust - Qiita

2026年1月26日 · にあえん

OpenCode + Ollamaで、Tool callがうまくいくモデルを模索している

こんにちは、ナナオです。 突然ですが、ローカルLLM使っていますか? 私は使いたいのですが、デスクトップもメモリもよわよわすぎて、なかなか使うのに奮闘しています。 今回は私が使ってきたモデルの一覧を紹介しようと思います。 使っているPCのスペックは以下の通り。 CPU: AMD Ryzen 9 3900 メモリ: 24GB GPU: GeForce GTX 1660 SUPER 使ったプロンプトは、opencodeの/initコマンドを実行した際の以下の一文(一部改変しています) Please analyze this codebase and create an AGENTS.md file containing: 1. Build/lint/test commands - especially for running a single test 2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc. The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 150 lines long. If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include them. If there's already an AGENTS.md, improve it if it's located in <プロジェクトディレクトリ> Pythonプロジェクト上で/initコマンドを実行し、プロジェクトディレクトリを読んでそれに合ったAGENTS.mdを出力するか検証しています。 ...

2026年1月25日 · にあえん

tailscaleを試してみる

こんにちは、ナナオです。 今回は巷で噂のtailscaleというVPNを家に導入して、外から安全に家のネットワークにアクセスできるようにしたいと思います。 初期設定 まずはアカウントを作ります。 以下のサイトにアクセスします。 Tailscale 各情報を入力して、デバイスのセットアップをします。 デバイスにtailscaleをインストールします。 windowsを使っている場合はwingetでインストール可能です。 winget install Tailscale.Tailscale インストールすると以下のような画面が立ち上がります。 次の画面で「Sign in your network」をクリックします。 以下のような画面がブラウザで立ち上がるので、Connectを押します。 成功すると、以下のような画面が表示されます。 これで一つ目のデバイスのセットアップは完了しました。 もう一つデバイスをセットアップしましょう。 家で動いているUbuntu Serverにインストールします。 先ほどの画面からadmin consoleに移動して、以下の「Skip this introduction」をクリックします。 SettingsタブのKeysから、Generate auth key…をクリックします。 項目を入力後Generate keyをクリックし、生成されたAPIキーを保存します。 いよいよlinuxにインストールします。 Ansibleで管理したいので、以下のようなプレイブックを実装しました。 --- - name: Install and Setup Tailscale hosts: main become: yes vars: tailscale_auth_key: "<先ほど生成したAPIキー>" tasks: - name: Ensure /usr/share/keyrings directory exists ansible.builtin.file: path: /usr/share/keyrings state: directory mode: "0755" - name: Download Tailscale GPG key ansible.builtin.get_url: url: https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg dest: /usr/share/keyrings/tailscale-archive-keyring.gpg mode: "0644" - name: Add Tailscale repository list ansible.builtin.get_url: url: https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list dest: /etc/apt/sources.list.d/tailscale.list mode: "0644" - name: Ensure Tailscale is installed ansible.builtin.apt: name: tailscale state: present update_cache: yes - name: Check if Tailscale is already authenticated ansible.builtin.command: tailscale status register: tailscale_status failed_when: false changed_when: false - name: Bring Tailscale up with Auth Key ansible.builtin.command: cmd: "tailscale up --authkey={{ tailscale_auth_key }}" # 既に接続済みの場合は実行しない(冪等性の確保) when: tailscale_status.rc != 0 これを実行します。 ...

2026年1月24日 · にあえん

M5Stamp C3U Mateが認識しないとき

こんにちは、ナナオです。 普段はあまり弄らないのですが、IoTデバイスを実装しようと思います。 以下の書籍を参考に、家でIoTボタンを実装しようとしています。 https://techbookfest.org/product/rwkYYevZRmhdfFGBksTeMk?productVariantID=ebjVWFS2sp9GKNFZCK3xwH ですが、なぜかうまくいかない。。というときに参考にしてください。 何がいかんかったか 書籍の手順でセットアップしたはずなのに、なぜかうまくいかない。。と思っていたら、正しい手順は以下にありました。 m5-docs つまり、買うデバイスを間違えていたのが原因でした。 誤: M5Stamp C3U Mate 正: M5Stamp C3 Mate いやー、、、一文字違いで完全にうっかりミスしていました。 USBドライブいろいろ入れてみたんですが、反応しなくて当然ですね。 感想 教訓: デバイス名はちゃんと確認しよう!!!!

2026年1月23日 · にあえん

hugoを予約投稿するための超ミニマムなCMSを作った

こんにちは、ナナオです。 私のブログはhugoというツールで投稿しているのですが、静的ビルドを行ってデプロイするため、予約投稿ができません。 よくある方法として、Github Actionsを使った方法がありますが、今回はRustを使った超ミニマムなCMSを作って予約投稿をしていきたいと思います。 仕様 CMSとは言っているものの、ミニマム実装の段階ではHugoのディレクトリ監視ツールのようなものです。 以下のような設定のconfig.yamlファイルを読み込み、dateの時間になったらhugoのビルドとデプロイを行います。 # 監視対象のhugoプロジェクトディレクトリパスを指定 workdir: path/to/hugo_project # スケジュールされた記事の情報 scheduled: - date: 2026/1/20 00:00:00 # 予約投稿する日時 file: posts/2026/01/test.md # 予約投稿する記事のディレクトリ(workdirからの相対パス) 実装 早速実装していきます。 実装は特にこだわりはないですが、やはり最高のプログラミング言語Rustで行います。テンション上がるしね。 use chrono::{Local, NaiveDateTime}; use regex::Regex; use serde::Deserialize; use std::collections::HashSet; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use std::thread; use std::time::Duration; // 設定ファイルの構造体定義 #[derive(Debug, Deserialize)] struct Config { workdir: String, scheduled: Vec<ScheduledPost>, } #[derive(Debug, Deserialize)] struct ScheduledPost { date: String, file: String, } fn main() { println!("Hugo Local CMS: 予約投稿監視を開始しました..."); // 処理済みファイルを記録するSet (日付+ファイル名 をキーにする) let mut processed: HashSet<String> = HashSet::new(); loop { println!("{} ファイルチェック開始", Local::now().to_rfc3339()); // 設定ファイルの読み込み let config = match load_config("config.yaml") { Ok(cfg) => cfg, Err(e) => { eprintln!("設定読み込みエラー: {}", e); thread::sleep(Duration::from_secs(10)); continue; } }; let now = chrono::Local::now().naive_local(); for post in config.scheduled { let key = format!("{}{}", post.date, post.file); // 既に処理済みならスキップ if processed.contains(&key) { continue; } // 日付パース (フォーマット: 2026/01/20 18:45:00) let pub_date = match NaiveDateTime::parse_from_str(&post.date, "%Y/%m/%d %H:%M:%S") { Ok(dt) => dt, Err(e) => { eprintln!("日付パースエラー ({}): {}", post.file, e); continue; } }; // 時間チェック if now > pub_date { println!("⏰ 予約時刻になりました: {}", post.file); // publish処理を実行 publish(&config.workdir, &post.file); // 処理済みリストに追加 processed.insert(key); } } println!("{} ファイルチェック終了", Local::now().to_rfc3339()); // 30秒待機 thread::sleep(Duration::from_secs(30)); } } fn load_config(path: &str) -> Result<Config, Box<dyn std::error::Error>> { let content = fs::read_to_string(path)?; let config: Config = serde_yaml::from_str(&content)?; Ok(config) } fn publish(work_dir: &str, relative_path: &str) { let full_path = Path::new(work_dir).join(relative_path); // 1. ファイルのドラフト解除 if let Err(e) = modify_draft_status(&full_path) { eprintln!("❌ ファイル修正失敗 [{:?}]: {}", full_path, e); return; } // 2. Hugoコマンド実行 execute_hugo(work_dir); } fn modify_draft_status(path: &PathBuf) -> std::io::Result<()> { // ファイル読み込み let content = fs::read_to_string(path)?; // 正規表現: 行頭の "draft = true" (スペース許容) を検索 // (?m) はマルチラインモード有効化 let re = Regex::new(r"(?m)^(draft\s*=\s*)true").unwrap(); if re.is_match(&content) { // 置換実行 ( ${1} は "draft = " の部分 ) let new_content = re.replace(&content, "${1}false"); // 書き込み fs::write(path, new_content.as_bytes())?; println!( "📝 Draftフラグを解除しました: {:?}", path.file_name().unwrap() ); } else { println!("ℹ️ Draftフラグの変更不要: {:?}", path.file_name().unwrap()); } Ok(()) } fn execute_hugo(work_dir: &str) { println!("🏗️ Hugoビルドを開始します..."); // 1. hugo if run_command(work_dir, "hugo", &[]).is_err() { return; } // 2. hugo deploy if run_command(work_dir, "hugo", &["deploy"]).is_err() { return; } println!("✅ Hugoのビルドとデプロイが完了しました。"); } fn run_command(dir: &str, command: &str, args: &[&str]) -> Result<(), ()> { println!("🚀 実行中: {} {:?} (in {})", command, args, dir); let status = Command::new(command) .args(args) .current_dir(dir) // 作業ディレクトリ指定 .status(); // 実行して終了ステータスを待つ match status { Ok(s) if s.success() => Ok(()), Ok(s) => { eprintln!("エラー: コマンドが失敗しました (Exit Code: {:?})", s.code()); Err(()) } Err(e) => { eprintln!("エラー: コマンドを実行できませんでした: {}", e); Err(()) } } } 実装内では30秒ごとにconfig.yamlを走査します。 ...

2026年1月22日 · にあえん

ollama + gemma3 + opencodeで最高のコーディング環境を手に入れる

前回の記事からの続きになります。 あれからGLM-4.7やMiniMax M2.1といった無料のモデルを使ってみたのですが、いまいちしっくりくる実装が出力されないことがよくありました。 ただ、Gemini 3 Flashといったモデルはすぐにトークンを使い切ってしまうため、びくびくしながらプロンプトを打っています。 そんなのは嫌だ!!!! ということで今回はollamaを使って今度こそ最高のコーディング環境を作りたいと思います。 ollamaのインストール ollamaのインストールからやっていきます。 WSLの環境に入れるので、以下のlinuxへのインストールを試します。 Linux - Ollama 以下のインストールスクリプトを実行します。 curl -fsSL https://ollama.com/install.sh | sh ちなみに最初miseからインストールを試みたのですが、こちらは失敗しました。 ❯ mise use -g ollama mise ollama@0.14.2 ⠁ 0smise ERROR Failed to install aqua:ollama/ollama@latest: no asset found: ollama-linux-amd64.tgz Available assets: ollama-darwin.tgz Ollama-darwin.zip ollama-linux-amd64-rocm.tar.zst ollama-linux-amd64.tar.zst ollama-linux-arm64-jetpack5.tar.zst ollama-linux-arm64-jetpack6.tar.zst ollama-linux-arm64.tar.zst ollama-windows-amd64-rocm.zip ollama-windows-amd64.zip ollama-windows-arm64.zip Ollama.dmg OllamaSetup.exe sha256sum.txt mise ERROR Run with --verbose or MISE_VERBOSE=1 for more information ということでmiseの公式リポジトリに質問として投げています。 Failed to install ollama · jdx/mise · Discussion #7758 · GitHub ...

2026年1月21日 · にあえん

OpenCode + Gemini(とGLM-4.7)を使って無料で最強のコーディングエージェントを手に入れる

こんにちは、ナナオです。 前回Gemini CLIをセットアップしましたが、今回はOpenCodeとGeminiを組み合わせて最強のコーディング環境を手に入れようと思います。 初期設定 まずはOpenCodeをインストールします。 miseでインストール可能です。 mise use -g opencode 使用するプロバイダを選択します。 Geminiを使いたいので、Googleを選択します。 ❯ opencode auth login ┌ Add credential │ ◆ Select provider │ Search: │ ○ OpenCode Zen │ ○ Anthropic │ ○ GitHub Copilot │ ○ OpenAI │ ● Google │ ○ OpenRouter │ ○ Vercel AI Gateway │ ... │ ↑/↓ to select • Enter: confirm • Type: to search └ APIキーを入力して完了です。 ❯ opencode auth login ┌ Add credential │ ◇ Select provider │ Google │ ◇ Enter your API key │ ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪ │ └ Done opencode auth listで追加したプロバイダにGoogleがいることを確認します。 ...

2026年1月20日 · にあえん