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
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@

## [Unreleased]

### Changed
- **シェル有効化を `bin/rc` の source に統一**しました (PLAN31_1)。`devbase init` 後に
いま開いているシェルへ devbase(PATH / 補完)を即時適用するには
`. ~/devbase/bin/rc`(= `source ~/devbase/bin/rc`)を使います。`bin/rc` は自身の
場所から `DEVBASE_ROOT` を解決するため、Python(uv)起動もコマンド置換 `$(...)`
も不要になり、ワンライナーは
`curl -fsSL https://dl.basex.jp/i | bash && . ~/devbase/bin/rc` で現在のシェルまで
有効化できます。

### Removed
- **`devbase shell-rc` サブコマンドを廃止**しました (PLAN31_1, 破壊的変更)。rc ファイル
パスを print して `source "$(devbase shell-rc)"` する方式は、上記の `. bin/rc` に
置き換えました。`source "$(devbase shell-rc)"` を使っているスクリプトは
`. <DEVBASE_ROOT>/bin/rc` に書き換えてください。

### Added
- **ワンライナー installer (`install.sh`) を新設**しました (PLAN31_1)。
`curl -fsSL https://dl.basex.jp/i | bash`
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ devbaseは、Docker Composeを使った再現性の高い開発環境を提供
### ワンライナーインストール(推奨)

```bash
curl -fsSL https://dl.basex.jp/i | bash
curl -fsSL https://dl.basex.jp/i | bash && . ~/devbase/bin/rc
```

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

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

環境変数で挙動を上書きできます。

Expand Down Expand Up @@ -59,7 +59,7 @@ DEVBASE_INSTALL_DIR=~/work/devbase DEVBASE_INSTALL_REF=v1.2.3 \
git clone https://github.com/devbasex/devbase.git
cd devbase
./bin/devbase init
source "$(./bin/devbase shell-rc)" # rc ファイル(zsh/bash on Linux/macOS)を自動判定して再読み込み
. ./bin/rc # いまのシェルで devbase を有効化(PATH / 補完)

# 2. Pluginのインストール
devbase plugin repo add user/repo # リポジトリ登録(init でサンプルレジストリ devbasex/devbase-samples は自動登録済み)
Expand Down Expand Up @@ -120,7 +120,7 @@ devbaseのコマンドは4つのグループにまとめられています。

- **ショートカット**: `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#ショートカットコマンド))
- **プレフィックス略記**: `devbase p l` → `devbase plugin list`
- **トップレベルコマンド**: `init`, `status`, `shell-rc`
- **トップレベルコマンド**: `init`, `status`

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

Expand Down
4 changes: 2 additions & 2 deletions bin/devbase
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ run_python() {
# Resolve abbreviated command to full command name via unique prefix matching
resolve_command() {
local input="$1"
local commands="init status shell-rc project container ct env plugin pl snapshot ss up down login build rebuild ps scale list help"
local commands="init status project container ct env plugin pl snapshot ss up down login build rebuild ps scale list help"
local matches=()
for cmd in $commands; do
[[ "$cmd" == "$input"* ]] && matches+=("$cmd")
Expand Down Expand Up @@ -355,7 +355,7 @@ case "$_resolved_cmd" in
# Python-implemented commands
--version|-V)
run_python "$@" ;;
init|status|shell-rc|project|container|ct|env|plugin|pl|snapshot|ss|up|down|login|ps|scale|rebuild|list)
init|status|project|container|ct|env|plugin|pl|snapshot|ss|up|down|login|ps|scale|rebuild|list)
run_python "${_resolved_cmd}" "${_DEVBASE_ARGS[@]}" ;;
# Shell-implemented commands
build) cmd_build "${_DEVBASE_ARGS[@]}" ;;
Expand Down
38 changes: 38 additions & 0 deletions bin/rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# devbase: いま開いているシェルで devbase を有効化する (source して使う)。
#
# . ~/devbase/bin/rc # bash / zsh 共通
#
# `devbase init` が rc ファイルに書き込む有効化 (DEVBASE_ROOT / PATH / 補完) を、
# 現在のシェルへ即時適用する。新しく開くシェルは init が rc に追記したブロックで
# 自動有効化されるため、本ファイルは「今のシェルにも通す」ためのもの。
#
# 注意: 実行ではなく source して使う。exit せず、シェルのオプションも変更しない。

# このファイル自身の場所から DEVBASE_ROOT を解決する。
# bash は ${BASH_SOURCE[0]}、zsh は $0 (FUNCTION_ARGZERO 既定で source 元のパス)。
_devbase_rc_src="${BASH_SOURCE[0]:-$0}"
_devbase_bin="$(CDPATH='' cd -- "$(dirname -- "$_devbase_rc_src")" && pwd)"
DEVBASE_ROOT="$(CDPATH='' cd -- "$_devbase_bin/.." && pwd)"
export DEVBASE_ROOT

# DEVBASE_ROOT/bin を PATH 先頭へ追加 (冪等)。
case ":$PATH:" in
*":$_devbase_bin:"*) ;;
*) PATH="$_devbase_bin:$PATH"; export PATH ;;
esac

# シェル補完を現在のシェルへ読み込む (init が rc に書く内容と同一)。
if [ -n "${ZSH_VERSION:-}" ]; then
# zsh の fpath は配列。$fpath は要素展開させたいので意図的に非クォート。
# shellcheck disable=SC2206
fpath=("$DEVBASE_ROOT/etc" $fpath)
autoload -Uz compinit && compinit
elif [ -n "${BASH_VERSION:-}" ]; then
if [ -f "$DEVBASE_ROOT/etc/devbase-completion.bash" ]; then
# shellcheck source=/dev/null
. "$DEVBASE_ROOT/etc/devbase-completion.bash"
fi
fi

unset _devbase_rc_src _devbase_bin
24 changes: 4 additions & 20 deletions docs/user/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ devbase のコマンドは 4 つのグループとトップレベルコマンド
graph TD
A[devbase] --> B[init]
A --> C[status]
A --> H[shell-rc]
A --> D[project]
A --> E[env]
A --> F[plugin / pl]
Expand Down Expand Up @@ -103,31 +102,16 @@ devbase status
- 環境変数の設定状況
- スナップショットの状態

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

`devbase init` が書き込んだシェル設定ファイル(rc ファイル)のフルパスを stdout に 1 行だけ出力します。`source` のコマンド置換と組み合わせ、ユーザーが zsh / bash on Linux / bash on macOS のどれを使っているかを意識せずに rc ファイルを再読み込みするためのユーティリティです。

```
devbase shell-rc
```

判定ロジックは `devbase init` と同一なので、書き込み先と完全に一致します。

| 環境 | 出力例 |
|------|--------|
| zsh | `/Users/<user>/.zshrc` |
| bash on macOS | `/Users/<user>/.bash_profile` |
| bash on Linux | `/home/<user>/.bashrc` |

使用例:
`devbase init` 後に **いま開いているシェル**で devbase(PATH / 補完)を即時有効化するための source 用スクリプトです。`devbase` のサブコマンドではなく、`bin/rc` を直接 source して使います。

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

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

## project グループ

Expand Down
16 changes: 6 additions & 10 deletions docs/user/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@ devbase を利用するには、以下のソフトウェアがホストマシン
手順 1〜2(クローンと初期化)を 1 コマンドで自動化できます。`git` と `curl` があれば実行できます。

```bash
curl -fsSL https://dl.basex.jp/i | bash
curl -fsSL https://dl.basex.jp/i | bash && . ~/devbase/bin/rc
```

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

1. `~/devbase` に devbase を clone します(既に devbase が clone 済みなら `git pull --ff-only` で更新)。
2. clone 先で `devbase init` を 1 回実行します(uv の自動導入・PATH/補完の登録・`plugins.yml` 生成を含む)。
3. 完了後、シェル再読み込み(手順 3)以降の次の手順を表示します。**新しく開くターミナルでは自動で有効**です。

**いま開いている端末で即使う**なら、末尾に `&& source "$(~/devbase/bin/devbase shell-rc)"` を付けます(`&&` 以降は呼び出し元シェルで実行されるため、その場で PATH が通ります)。
3. 末尾の `&& . ~/devbase/bin/rc` を**いま開いている端末**で実行し(`&&` 以降はパイプのサブシェルではなく呼び出し元シェルで動くため)、その端末で即座に `devbase`(PATH / 補完)が使える状態にします。新しく開くターミナルは init の rc 追記により自動で有効です。

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

Expand Down Expand Up @@ -78,17 +76,15 @@ cd devbase

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

### 3. シェルの再読み込み
### 3. シェルで有効化

```bash
source "$(./bin/devbase shell-rc)"
. ./bin/rc
```

`devbase shell-rc` は `init` が書き込んだ rc ファイルのパスを 1 行で出力します。コマンド置換と組み合わせることで、環境差を意識せずに 1 行で再読み込みできます。

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

> **Note:** 新しいターミナルを開いても同様に反映されます
> **Note:** 新しいターミナルを開いた場合は `init` が rc に追記したブロックで自動的に有効化されるため、この手順は不要です

### 4. プラグインリポジトリの登録

Expand Down
4 changes: 2 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ print_next_steps() {
配置先: ${INSTALL_DIR}
------------------------------------------------------------
次の手順:
1. シェルを再読み込み:
source "\$("${INSTALL_DIR}/bin/devbase" shell-rc)"
1. いまのシェルで有効化:
. "${INSTALL_DIR}/bin/rc"
2. plugin を導入:
devbase plugin install <name>
3. プロジェクトへ移動して env を初期化 (対話):
Expand Down
11 changes: 1 addition & 10 deletions lib/devbase/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,10 +488,6 @@ def _create_parser():
# --- Top-level commands ---
subparsers.add_parser('init', help='Initialize devbase environment')
subparsers.add_parser('status', help='Show overall status')
subparsers.add_parser(
'shell-rc',
help='Print shell RC file path (e.g. source "$(devbase shell-rc)")'
)

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

Expand Down Expand Up @@ -607,11 +603,6 @@ def _dispatch(cmd, args):
from devbase.commands.container import cmd_container
return cmd_container(args)

# --- Commands not requiring DEVBASE_ROOT ---
if cmd == 'shell-rc':
from devbase.commands.shell_rc import cmd_shell_rc
return cmd_shell_rc()

# --- Commands requiring DEVBASE_ROOT ---
devbase_root = _require_devbase_root()

Expand Down
15 changes: 0 additions & 15 deletions lib/devbase/commands/shell_rc.py

This file was deleted.

73 changes: 73 additions & 0 deletions tests/cli/test_bin_rc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""bin/rc(シェル有効化スクリプト)のテスト (PLAN31_1)。

`. bin/rc` を source すると、いま開いているシェルへ devbase の有効化
(`DEVBASE_ROOT` の設定 / `DEVBASE_ROOT/bin` の PATH 追加)が即時適用される
ことを検証する。`devbase shell-rc`(廃止)+ `source "$(...)"` を置き換えた
軽量パス(Python/uv 起動なし・コマンド置換なし)であることが要点。

補完の読み込みはシェル種別依存のため、ここでは PATH / DEVBASE_ROOT と冪等性の
みを検証する(補完登録ロジックは init テストの責務)。
"""

import os
import shutil
import subprocess
import sys
from pathlib import Path

import pytest

REPO_ROOT = Path(__file__).resolve().parents[2]
RC = REPO_ROOT / "bin" / "rc"
BIN = REPO_ROOT / "bin"
BASH = shutil.which("bash") or "/bin/bash"


def _source_rc(snippet: str) -> subprocess.CompletedProcess:
"""クリーンな環境で bin/rc を source し、続けて snippet を実行する。"""
env = {**os.environ}
env.pop("DEVBASE_ROOT", None)
script = f'. "{RC}"\n{snippet}'
return subprocess.run(
[BASH, "-c", script], capture_output=True, text=True, env=env,
)


def test_rc_file_exists():
assert RC.exists(), "bin/rc が存在すること"


def test_sourcing_sets_devbase_root():
r = _source_rc('printf "ROOT=%s\\n" "$DEVBASE_ROOT"')
assert r.returncode == 0, r.stderr
assert f"ROOT={REPO_ROOT}" in r.stdout, r.stdout


def test_sourcing_prepends_bin_to_path():
r = _source_rc('printf "PATH=%s\\n" "$PATH"')
assert r.returncode == 0, r.stderr
path_value = next(
line[len("PATH="):] for line in r.stdout.splitlines()
if line.startswith("PATH=")
)
assert f":{BIN}:" in f":{path_value}:", f"{BIN} が PATH に含まれること: {path_value}"


def test_devbase_resolves_after_sourcing():
"""source 後に `devbase` 実行ファイルが PATH 経由で解決できること。"""
r = _source_rc('command -v devbase')
assert r.returncode == 0, r.stderr
assert r.stdout.strip() == str(BIN / "devbase"), r.stdout


def test_path_addition_is_idempotent():
"""2 回 source しても bin が PATH に重複追加されないこと。"""
r = _source_rc(f'. "{RC}"\nprintf "%s" "$PATH"')
assert r.returncode == 0, r.stderr
count = (":" + r.stdout + ":").count(f":{BIN}:")
assert count == 1, f"{BIN} は PATH に 1 回だけ: count={count}\n{r.stdout}"


if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-v"]))
Loading