Skip to content

Commit f9107a3

Browse files
takemi-ohamaclaude
andcommitted
feat(shell): bin/rc 新設で現在シェル有効化を統一し devbase shell-rc を廃止 (PLAN31_1)
破壊的変更: `devbase shell-rc` サブコマンドを削除。rc パスを print して `source "$(devbase shell-rc)"` する方式 (Python/uv 起動 + コマンド置換) を、 source 用の軽量スクリプト `bin/rc` に置き換える。 - bin/rc: 自身の場所から DEVBASE_ROOT を自己解決し、DEVBASE_ROOT/bin を PATH へ 追加 (冪等) + 補完を読み込む (bash/zsh 対応・init が rc に書く内容と同一)。 `. ~/devbase/bin/rc` で現在のシェルを即時有効化できる。 - bin/devbase / cli.py: shell-rc の dispatch・parser・command list を削除。 shell_rc.py を削除 (get_shell_rc_file は init が使うため utils に残置)。 - install.sh 完了案内・README・getting-started・cli-reference のワンライナー/ 手順を `. bin/rc` に更新。ワンライナーは `curl -fsSL https://dl.basex.jp/i | bash && . ~/devbase/bin/rc`。 - tests/cli/test_bin_rc.py: source で DEVBASE_ROOT/PATH 設定・devbase 解決・ PATH 冪等を検証 (5 件)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 12c86fd commit f9107a3

10 files changed

Lines changed: 146 additions & 64 deletions

File tree

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44

55
## [Unreleased]
66

7+
### Changed
8+
- **シェル有効化を `bin/rc` の source に統一**しました (PLAN31_1)。`devbase init` 後に
9+
いま開いているシェルへ devbase(PATH / 補完)を即時適用するには
10+
`. ~/devbase/bin/rc`(= `source ~/devbase/bin/rc`)を使います。`bin/rc` は自身の
11+
場所から `DEVBASE_ROOT` を解決するため、Python(uv)起動もコマンド置換 `$(...)`
12+
も不要になり、ワンライナーは
13+
`curl -fsSL https://dl.basex.jp/i | bash && . ~/devbase/bin/rc` で現在のシェルまで
14+
有効化できます。
15+
16+
### Removed
17+
- **`devbase shell-rc` サブコマンドを廃止**しました (PLAN31_1, 破壊的変更)。rc ファイル
18+
パスを print して `source "$(devbase shell-rc)"` する方式は、上記の `. bin/rc`
19+
置き換えました。`source "$(devbase shell-rc)"` を使っているスクリプトは
20+
`. <DEVBASE_ROOT>/bin/rc` に書き換えてください。
21+
722
### Added
823
- **ワンライナー installer (`install.sh`) を新設**しました (PLAN31_1)。
924
`curl -fsSL https://dl.basex.jp/i | bash`

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ devbaseは、Docker Composeを使った再現性の高い開発環境を提供
2323
### ワンライナーインストール(推奨)
2424

2525
```bash
26-
curl -fsSL https://dl.basex.jp/i | bash
26+
curl -fsSL https://dl.basex.jp/i | bash && . ~/devbase/bin/rc
2727
```
2828

29-
`~/devbase` に clone(既存なら更新)し`devbase init` まで自動実行します(uv の自動導入・PATH/補完の登録・`plugins.yml` 生成を含む)**新しく開くターミナルでは自動で有効**です
29+
`~/devbase` に clone(既存なら更新)し `devbase init` まで自動実行(uv の自動導入・PATH/補完の登録・`plugins.yml` 生成を含む)したうえで、末尾の `&& . ~/devbase/bin/rc`**いま開いている端末**にも devbase を通します(`&&` 以降はパイプのサブシェルではなく呼び出し元シェルで実行されるため、その場で `devbase` が使えます)。新しく開くターミナルは init の rc 追記により自動で有効です
3030

31-
**いま開いている端末で即使う**なら、末尾に `&& source "$(~/devbase/bin/devbase shell-rc)"` を付けます(`&&` 以降は呼び出し元シェルで実行されるため、その場で PATH が通ります)。配置先を `DEVBASE_INSTALL_DIR` で変えた場合は同パスに合わせてください
31+
> 配置先を `DEVBASE_INSTALL_DIR` で変えた場合は後半の `~/devbase/bin/rc` も同じパスに合わせてください。`. ~/devbase/bin/rc` を省いた `... | bash` だけでも導入は完了します(その場合は完了メッセージの案内に従ってください)
3232
3333
環境変数で挙動を上書きできます。
3434

@@ -59,7 +59,7 @@ DEVBASE_INSTALL_DIR=~/work/devbase DEVBASE_INSTALL_REF=v1.2.3 \
5959
git clone https://github.com/devbasex/devbase.git
6060
cd devbase
6161
./bin/devbase init
62-
source "$(./bin/devbase shell-rc)" # rc ファイル(zsh/bash on Linux/macOS)を自動判定して再読み込み
62+
. ./bin/rc # いまのシェルで devbase を有効化(PATH / 補完)
6363
6464
# 2. Pluginのインストール
6565
devbase plugin repo add user/repo # リポジトリ登録(init でサンプルレジストリ devbasex/devbase-samples は自動登録済み)
@@ -120,7 +120,7 @@ devbaseのコマンドは4つのグループにまとめられています。
120120
121121
- **ショートカット**: `up [name]`, `down [name]`, `login [index]`, `build [image]`, `ps [name]`, `scale [name] <num>`, `list` はトップレベルから直接使用可能(`project` グループへ自動転送。`logs` はシノニムを持ちません)。ただし `build` のみ例外で、`project` グループ(Python 実装)ではなく `bin/devbase` のシェル実装 `cmd_build` へ直接委譲されます(詳細は [CLI リファレンス](docs/user/cli-reference.md#ショートカットコマンド)
122122
- **プレフィックス略記**: `devbase p l``devbase plugin list`
123-
- **トップレベルコマンド**: `init`, `status`, `shell-rc`
123+
- **トップレベルコマンド**: `init`, `status`
124124

125125
全コマンドの構文・オプション・使用例は [CLIリファレンス](docs/user/cli-reference.md) を参照してください。
126126

bin/devbase

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ run_python() {
199199
# Resolve abbreviated command to full command name via unique prefix matching
200200
resolve_command() {
201201
local input="$1"
202-
local commands="init status shell-rc project container ct env plugin pl snapshot ss up down login build rebuild ps scale list help"
202+
local commands="init status project container ct env plugin pl snapshot ss up down login build rebuild ps scale list help"
203203
local matches=()
204204
for cmd in $commands; do
205205
[[ "$cmd" == "$input"* ]] && matches+=("$cmd")
@@ -355,7 +355,7 @@ case "$_resolved_cmd" in
355355
# Python-implemented commands
356356
--version|-V)
357357
run_python "$@" ;;
358-
init|status|shell-rc|project|container|ct|env|plugin|pl|snapshot|ss|up|down|login|ps|scale|rebuild|list)
358+
init|status|project|container|ct|env|plugin|pl|snapshot|ss|up|down|login|ps|scale|rebuild|list)
359359
run_python "${_resolved_cmd}" "${_DEVBASE_ARGS[@]}" ;;
360360
# Shell-implemented commands
361361
build) cmd_build "${_DEVBASE_ARGS[@]}" ;;

bin/rc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env bash
2+
# devbase: いま開いているシェルで devbase を有効化する (source して使う)。
3+
#
4+
# . ~/devbase/bin/rc # bash / zsh 共通
5+
#
6+
# `devbase init` が rc ファイルに書き込む有効化 (DEVBASE_ROOT / PATH / 補完) を、
7+
# 現在のシェルへ即時適用する。新しく開くシェルは init が rc に追記したブロックで
8+
# 自動有効化されるため、本ファイルは「今のシェルにも通す」ためのもの。
9+
#
10+
# 注意: 実行ではなく source して使う。exit せず、シェルのオプションも変更しない。
11+
12+
# このファイル自身の場所から DEVBASE_ROOT を解決する。
13+
# bash は ${BASH_SOURCE[0]}、zsh は $0 (FUNCTION_ARGZERO 既定で source 元のパス)。
14+
_devbase_rc_src="${BASH_SOURCE[0]:-$0}"
15+
_devbase_bin="$(CDPATH='' cd -- "$(dirname -- "$_devbase_rc_src")" && pwd)"
16+
DEVBASE_ROOT="$(CDPATH='' cd -- "$_devbase_bin/.." && pwd)"
17+
export DEVBASE_ROOT
18+
19+
# DEVBASE_ROOT/bin を PATH 先頭へ追加 (冪等)。
20+
case ":$PATH:" in
21+
*":$_devbase_bin:"*) ;;
22+
*) PATH="$_devbase_bin:$PATH"; export PATH ;;
23+
esac
24+
25+
# シェル補完を現在のシェルへ読み込む (init が rc に書く内容と同一)。
26+
if [ -n "${ZSH_VERSION:-}" ]; then
27+
# zsh の fpath は配列。$fpath は要素展開させたいので意図的に非クォート。
28+
# shellcheck disable=SC2206
29+
fpath=("$DEVBASE_ROOT/etc" $fpath)
30+
autoload -Uz compinit && compinit
31+
elif [ -n "${BASH_VERSION:-}" ]; then
32+
if [ -f "$DEVBASE_ROOT/etc/devbase-completion.bash" ]; then
33+
# shellcheck source=/dev/null
34+
. "$DEVBASE_ROOT/etc/devbase-completion.bash"
35+
fi
36+
fi
37+
38+
unset _devbase_rc_src _devbase_bin

docs/user/cli-reference.md

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ devbase のコマンドは 4 つのグループとトップレベルコマンド
1010
graph TD
1111
A[devbase] --> B[init]
1212
A --> C[status]
13-
A --> H[shell-rc]
1413
A --> D[project]
1514
A --> E[env]
1615
A --> F[plugin / pl]
@@ -103,31 +102,16 @@ devbase status
103102
- 環境変数の設定状況
104103
- スナップショットの状態
105104

106-
### `devbase shell-rc`
105+
### `bin/rc`(いまのシェルで有効化)
107106

108-
`devbase init` が書き込んだシェル設定ファイル(rc ファイル)のフルパスを stdout に 1 行だけ出力します。`source` のコマンド置換と組み合わせ、ユーザーが zsh / bash on Linux / bash on macOS のどれを使っているかを意識せずに rc ファイルを再読み込みするためのユーティリティです。
109-
110-
```
111-
devbase shell-rc
112-
```
113-
114-
判定ロジックは `devbase init` と同一なので、書き込み先と完全に一致します。
115-
116-
| 環境 | 出力例 |
117-
|------|--------|
118-
| zsh | `/Users/<user>/.zshrc` |
119-
| bash on macOS | `/Users/<user>/.bash_profile` |
120-
| bash on Linux | `/home/<user>/.bashrc` |
121-
122-
使用例:
107+
`devbase init` 後に **いま開いているシェル**で devbase(PATH / 補完)を即時有効化するための source 用スクリプトです。`devbase` のサブコマンドではなく、`bin/rc` を直接 source して使います。
123108

124109
```bash
125-
# 初期化直後に rc を再読み込み(環境差を吸収)
126110
./bin/devbase init
127-
source "$(./bin/devbase shell-rc)"
111+
. ./bin/rc # = source ./bin/rc (bash / zsh 共通)
128112
```
129113

130-
> **⚠ 引用符は必須**: `source $(devbase shell-rc)` のように引用符を省くと、ホームディレクトリ名に空白を含む環境(例: `/Users/foo bar/.zshrc`)で word splitting が起き `source` が失敗します。必ず `source "$(devbase shell-rc)"` の形で書いてください
114+
`bin/rc` は自身の場所から `DEVBASE_ROOT` を解決し、`DEVBASE_ROOT/bin` を PATH へ追加(冪等)したうえで、シェル補完を読み込みます(`init` が rc ファイルへ追記する有効化と同じ内容)。新しく開くシェルは init が rc に追記したブロックで自動有効化されるため、この手順は不要です
131115

132116
## project グループ
133117

docs/user/getting-started.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,14 @@ devbase を利用するには、以下のソフトウェアがホストマシン
2222
手順 1〜2(クローンと初期化)を 1 コマンドで自動化できます。`git``curl` があれば実行できます。
2323

2424
```bash
25-
curl -fsSL https://dl.basex.jp/i | bash
25+
curl -fsSL https://dl.basex.jp/i | bash && . ~/devbase/bin/rc
2626
```
2727

2828
このコマンドは次を行います。
2929

3030
1. `~/devbase` に devbase を clone します(既に devbase が clone 済みなら `git pull --ff-only` で更新)。
3131
2. clone 先で `devbase init` を 1 回実行します(uv の自動導入・PATH/補完の登録・`plugins.yml` 生成を含む)。
32-
3. 完了後、シェル再読み込み(手順 3)以降の次の手順を表示します。**新しく開くターミナルでは自動で有効**です。
33-
34-
**いま開いている端末で即使う**なら、末尾に `&& source "$(~/devbase/bin/devbase shell-rc)"` を付けます(`&&` 以降は呼び出し元シェルで実行されるため、その場で PATH が通ります)。
32+
3. 末尾の `&& . ~/devbase/bin/rc`**いま開いている端末**で実行し(`&&` 以降はパイプのサブシェルではなく呼び出し元シェルで動くため)、その端末で即座に `devbase`(PATH / 補完)が使える状態にします。新しく開くターミナルは init の rc 追記により自動で有効です。
3533

3634
`env init`(手順 7)は対話が必要なため、ワンライナーでは**実行せず案内のみ**です。完了後に手動で実行してください。配置先を `DEVBASE_INSTALL_DIR` で変えた場合は、`~/devbase/...` を同じパスに合わせてください。
3735

@@ -78,17 +76,15 @@ cd devbase
7876

7977
書き込み先は現在のシェル種別と OS から自動判定されます(zsh → `~/.zshrc`、bash on macOS → `~/.bash_profile`、bash on Linux → `~/.bashrc`)。
8078

81-
### 3. シェルの再読み込み
79+
### 3. シェルで有効化
8280

8381
```bash
84-
source "$(./bin/devbase shell-rc)"
82+
. ./bin/rc
8583
```
8684

87-
`devbase shell-rc``init` が書き込んだ rc ファイルのパスを 1 行で出力します。コマンド置換と組み合わせることで、環境差を意識せずに 1 行で再読み込みできます。
88-
89-
> **⚠ 引用符は必須**: `source $(./bin/devbase shell-rc)` のように引用符を省くと、ホームディレクトリ名に空白を含む環境(例: `/Users/foo bar/.zshrc`)で word splitting が起き `source` が失敗します。必ず `source "$(...)"` の形で書いてください。
85+
`bin/rc` を source すると、いま開いているシェルに devbase の PATH と補完がその場で適用されます(`init` が rc ファイルに追記する内容と同じ有効化を、現在のシェルへ即時反映します)。`devbase` 前提シェルである bash / zsh のどちらでも同じく `.`(= `source`)で読み込めます。
9086

91-
> **Note:** 新しいターミナルを開いても同様に反映されます
87+
> **Note:** 新しいターミナルを開いた場合は `init` が rc に追記したブロックで自動的に有効化されるため、この手順は不要です
9288
9389
### 4. プラグインリポジトリの登録
9490

install.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ print_next_steps() {
7979
配置先: ${INSTALL_DIR}
8080
------------------------------------------------------------
8181
次の手順:
82-
1. シェルを再読み込み:
83-
source "\$("${INSTALL_DIR}/bin/devbase" shell-rc)"
82+
1. いまのシェルで有効化:
83+
. "${INSTALL_DIR}/bin/rc"
8484
2. plugin を導入:
8585
devbase plugin install <name>
8686
3. プロジェクトへ移動して env を初期化 (対話):

lib/devbase/cli.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -488,10 +488,6 @@ def _create_parser():
488488
# --- Top-level commands ---
489489
subparsers.add_parser('init', help='Initialize devbase environment')
490490
subparsers.add_parser('status', help='Show overall status')
491-
subparsers.add_parser(
492-
'shell-rc',
493-
help='Print shell RC file path (e.g. source "$(devbase shell-rc)")'
494-
)
495491

496492
_add_project_parser(subparsers)
497493
_add_container_parser(subparsers)
@@ -531,7 +527,7 @@ def _expand_argv():
531527
# `build` はトップレベルショートカットから除外 (SHORTCUTS の注記参照)。
532528
# bin/devbase が build を shell 実装に委譲するため Python 側には top-level
533529
# build parser が無い。project build / container build は引き続き利用可能。
534-
commands = ['init', 'status', 'shell-rc', 'project', 'container', 'ct', 'env', 'plugin', 'pl',
530+
commands = ['init', 'status', 'project', 'container', 'ct', 'env', 'plugin', 'pl',
535531
'snapshot', 'ss', 'up', 'down', 'login', 'ps', 'scale', 'rebuild', 'list', 'help']
536532
repo_subcmds = ['add', 'remove', 'list', 'refresh']
537533

@@ -607,11 +603,6 @@ def _dispatch(cmd, args):
607603
from devbase.commands.container import cmd_container
608604
return cmd_container(args)
609605

610-
# --- Commands not requiring DEVBASE_ROOT ---
611-
if cmd == 'shell-rc':
612-
from devbase.commands.shell_rc import cmd_shell_rc
613-
return cmd_shell_rc()
614-
615606
# --- Commands requiring DEVBASE_ROOT ---
616607
devbase_root = _require_devbase_root()
617608

lib/devbase/commands/shell_rc.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/cli/test_bin_rc.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env python3
2+
"""bin/rc(シェル有効化スクリプト)のテスト (PLAN31_1)。
3+
4+
`. bin/rc` を source すると、いま開いているシェルへ devbase の有効化
5+
(`DEVBASE_ROOT` の設定 / `DEVBASE_ROOT/bin` の PATH 追加)が即時適用される
6+
ことを検証する。`devbase shell-rc`(廃止)+ `source "$(...)"` を置き換えた
7+
軽量パス(Python/uv 起動なし・コマンド置換なし)であることが要点。
8+
9+
補完の読み込みはシェル種別依存のため、ここでは PATH / DEVBASE_ROOT と冪等性の
10+
みを検証する(補完登録ロジックは init テストの責務)。
11+
"""
12+
13+
import os
14+
import shutil
15+
import subprocess
16+
import sys
17+
from pathlib import Path
18+
19+
import pytest
20+
21+
REPO_ROOT = Path(__file__).resolve().parents[2]
22+
RC = REPO_ROOT / "bin" / "rc"
23+
BIN = REPO_ROOT / "bin"
24+
BASH = shutil.which("bash") or "/bin/bash"
25+
26+
27+
def _source_rc(snippet: str) -> subprocess.CompletedProcess:
28+
"""クリーンな環境で bin/rc を source し、続けて snippet を実行する。"""
29+
env = {**os.environ}
30+
env.pop("DEVBASE_ROOT", None)
31+
script = f'. "{RC}"\n{snippet}'
32+
return subprocess.run(
33+
[BASH, "-c", script], capture_output=True, text=True, env=env,
34+
)
35+
36+
37+
def test_rc_file_exists():
38+
assert RC.exists(), "bin/rc が存在すること"
39+
40+
41+
def test_sourcing_sets_devbase_root():
42+
r = _source_rc('printf "ROOT=%s\\n" "$DEVBASE_ROOT"')
43+
assert r.returncode == 0, r.stderr
44+
assert f"ROOT={REPO_ROOT}" in r.stdout, r.stdout
45+
46+
47+
def test_sourcing_prepends_bin_to_path():
48+
r = _source_rc('printf "PATH=%s\\n" "$PATH"')
49+
assert r.returncode == 0, r.stderr
50+
path_value = next(
51+
line[len("PATH="):] for line in r.stdout.splitlines()
52+
if line.startswith("PATH=")
53+
)
54+
assert f":{BIN}:" in f":{path_value}:", f"{BIN} が PATH に含まれること: {path_value}"
55+
56+
57+
def test_devbase_resolves_after_sourcing():
58+
"""source 後に `devbase` 実行ファイルが PATH 経由で解決できること。"""
59+
r = _source_rc('command -v devbase')
60+
assert r.returncode == 0, r.stderr
61+
assert r.stdout.strip() == str(BIN / "devbase"), r.stdout
62+
63+
64+
def test_path_addition_is_idempotent():
65+
"""2 回 source しても bin が PATH に重複追加されないこと。"""
66+
r = _source_rc(f'. "{RC}"\nprintf "%s" "$PATH"')
67+
assert r.returncode == 0, r.stderr
68+
count = (":" + r.stdout + ":").count(f":{BIN}:")
69+
assert count == 1, f"{BIN} は PATH に 1 回だけ: count={count}\n{r.stdout}"
70+
71+
72+
if __name__ == "__main__":
73+
sys.exit(pytest.main([__file__, "-v"]))

0 commit comments

Comments
 (0)