Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: CI

on:
push:
branches: [master]
pull_request:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Syntax check shell scripts
run: |
# リポジトリ内の追跡対象シェルスクリプトを網羅的に構文チェックする。
# shebang を見てインタプリタを判定する:
# - bash shebang -> bash -n
# - #!/bin/sh (= dash) -> sh -n (bash -n は dash で落ちる構文を見逃す)
# - shebang なし -> bash -n (env.sh / env.d/* は bash/zsh が source
# する断片で bash 構文を含むため sh -n だと誤検知)
st=0
while IFS= read -r f; do
case "$(head -n1 "$f")" in
*bash*) checker="bash -n" ;;
'#!'*sh*) checker="sh -n" ;;
*) checker="bash -n" ;;
esac
echo "==> $checker $f"
$checker "$f" || st=1
done < <(git ls-files '*.sh')
exit $st

ubuntu-setup:
# Codespaces / WSL の Ubuntu セットアップ (apt + 並列 *env install) を実走する
runs-on: ubuntu-latest
timeout-minutes: 60
env:
DOTFILES_SETUP_LOGDIR: /tmp/dotfiles-setup-logs
steps:
- uses: actions/checkout@v4

- name: Run Ubuntu setup
run: ./script/setup-ubuntu.sh

- name: Verify installed versions
run: |
set -euo pipefail
nodenv="$HOME/.nodenv/bin/nodenv"
rbenv="$HOME/.rbenv/bin/rbenv"
pyenv="$HOME/.pyenv/bin/pyenv"
goenv="$HOME/.goenv/bin/goenv"

# ピン留めバージョンは各 setup スクリプトの VERSION= から抽出し、
# ここでハードコードしないことで定義のドリフトを防ぐ。
expected_version() {
sed -n 's/^VERSION=\(.*\)$/\1/p' "$1" | head -n1
}

st=0
assert_version() {
name="$1"; expected="$2"; actual="$3"
if [ "$expected" = "$actual" ]; then
echo "✓ $name: $actual (expected $expected)"
else
echo "✗ $name: got '$actual', expected '$expected'"
st=1
fi
}

node_expected="$(expected_version setup.d/ubuntu/002-nodenv.sh)"
ruby_expected="$(expected_version setup.d/ubuntu/003-rbenv.sh)"
python_expected="$(expected_version setup.d/ubuntu/004-pyenv.sh)"
go_expected="$(expected_version setup.d/ubuntu/005-goenv.sh)"

# *env exec は PATH/shims に依存せず、各 *env が選択中バージョンの管理下
# バイナリを直接実行する。ただし *env はカレントディレクトリ階層の
# .ruby-version 等のローカル pin を global より優先する。このリポジトリ自身は
# ルートに `.ruby-version = system` を持つため、setup が入れた global を検証
# するには中立なディレクトリ ($HOME) で実行する必要がある。
# node --version → "v24.16.0" の先頭 v を除去
assert_version node "$node_expected" "$(cd "$HOME" && "$nodenv" exec node --version | sed 's/^v//')"
# ruby --version → "ruby 4.0.5 (...) ..." の 2 フィールド目
assert_version ruby "$ruby_expected" "$(cd "$HOME" && "$rbenv" exec ruby --version | awk '{print $2}')"
# python --version → "Python 3.14.6" の 2 フィールド目
assert_version python "$python_expected" "$(cd "$HOME" && "$pyenv" exec python --version | awk '{print $2}')"
# go version → "go version go1.26.4 linux/amd64" の go プレフィックスを除去
assert_version go "$go_expected" "$(cd "$HOME" && "$goenv" exec go version | awk '{print $3}' | sed 's/^go//')"

# 不一致時は原因切り分け用に各 *env の選択状況をダンプする
if [ "$st" -ne 0 ]; then
echo "--- diagnostics ---"
"$rbenv" version || true
"$rbenv" which ruby || true
"$goenv" version || true
fi

gcloud --version

exit $st

- name: Upload setup logs
if: always()
uses: actions/upload-artifact@v4
with:
name: setup-logs
path: /tmp/dotfiles-setup-logs/
if-no-files-found: ignore
54 changes: 54 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# AGENTS.md

このリポジトリで作業する AI エージェント向けのガイド。リポジトリ固有のハマりどころを集約する。

## シェル / 実行環境

- **デフォルトシェルは zsh**(エージェントの Bash ツールも zsh で動く)。スクリプト内で
`status` を変数名に使わないこと — zsh では `status` は読み取り専用で `read-only variable`
エラーになる。`st` などに置き換える。
- `jq` のフィルタで `!=` を使うと zsh が `\!=` にエスケープしてパースエラーになる。
`select(.x | . == null | not)` のように `| not` パターンで書く。

## シェルスクリプトの構文チェック / lint

- `env.sh` と `env.d/**/*.sh` は **shebang を持たない**。シェル起動時に source される断片で、
bash/zsh 構文(関数定義・`[[ ]]` 等)を含む。したがって `sh -n`(dash)では誤検知する。
これらは `bash -n` で検査する。
- CI(`.github/workflows/ci.yml` の lint ジョブ)は追跡対象の全 `*.sh` を `git ls-files` で
列挙し、shebang で判定する: `*bash*` → `bash -n`、`#!...sh` → `sh -n`、**shebang なし →
`bash -n`**(上記の source 断片のため)。

## *env(言語ランタイム)

- バージョンのピン留めは `setup.d/ubuntu/00X-*.sh` の `VERSION=` に一元化されている
(nodenv=002 / rbenv=003 / pyenv=004 / goenv=005)。変更はこのファイルだけを書き換える。
- CI はインストール結果がこのピン留め値と一致するかをアサートする(不一致で fail)。期待値は
各 setup スクリプトの `VERSION=` から抽出するのでハードコードしない。
- **バージョン検証時は shims を PATH 先頭に置くこと。** `rbenv init -` / `goenv init -` は
shims を PATH 先頭に入れない実装があり、ランナー同梱の system ruby/go が優先されてしまう。
`export PATH="$HOME/.rbenv/shims:$HOME/.goenv/shims:...:$PATH"` のように明示する
(nodenv/pyenv は `init -` だけで効く)。
- リポジトリ更新は `git clone` 以外(パッケージ・手動展開・symlink)で配置された場合に備え、
`[ -d ~/.Xenv/.git ]` ガードで囲み `git pull --ff-only`(意図しない merge を作らない)。

## GnuPG

- `~/.gnupg` は `rc.d/gnupg` への symlink。`gpg-agent.conf` は **生成物で gitignore 対象**。
ソースは `rc.d/gnupg/gpg-agent.conf.linux` / `.darwin` で、`setup.d/dotfiles.sh` が
プラットフォーム別にコピー生成する。設定変更は生成物ではなく `.linux` / `.darwin` を編集する。
キャッシュ TTL は 1 年(一度 unlock すれば保持)。
- コミットは GPG 署名される(鍵 `036459B1`)。エージェントの非 tty シェルでは対話的 pinentry が
動かずコミットがハングする。**セッション開始時に一度ユーザーが**
`echo | gpg --clearsign -u 036459B1 -o /dev/null` を実行してパスフレーズを agent に
キャッシュさせてから、コミット系の作業を進める。

## dotfiles の symlink 規約

- プラットフォーム固有ファイルは `*.darwin` / `*.linux` サフィックスで管理し、
`setup.d/dotfiles.sh` の `resolve_os_name` が OS に応じて実体名へ解決する。
- push は必ず `git push origin <current-branch>`(bare push 禁止)。

## このファイルと CLAUDE.md

- `CLAUDE.md` はこの `AGENTS.md` への symlink。内容は AGENTS.md 側を編集する。
1 change: 1 addition & 0 deletions CLAUDE.md
Loading
Loading