Skip to content

Instantly share code, notes, and snippets.

@BonyChops
Last active December 5, 2025 07:44
Show Gist options
  • Select an option

  • Save BonyChops/fecc50786cb36dc286ebbc3b5c33a3fa to your computer and use it in GitHub Desktop.

Select an option

Save BonyChops/fecc50786cb36dc286ebbc3b5c33a3fa to your computer and use it in GitHub Desktop.

Manage .env with Bitwarden

Japanese: https://zenn.dev/bony_chops/articles/25024f5b13d7a6

Setup12

  1. Run

    # Bitwarden CLI installition, you can use other options as well
    npm install -g @bitwarden/cli
    
    # keymaster installition
    cd $(mktemp -d)
    git clone --depth 1 https://github.com/BonyChops/keymaster.git
    cd keymaster
    swiftc keymaster.swift -o keymaster
    mkdir -p ~/.local/bin
    mv keymaster ~/.local/bin
    cd
    
    # Set path to ~/.local/bin if you haven't
    
    # bw.sh, bw-env.sh download
    mkdir -p ~/scripts
    wget -O ~/scripts/bw.sh https://gist.github.com/BonyChops/fecc50786cb36dc286ebbc3b5c33a3fa/raw/bw.sh
    wget -O ~/scripts/bw-env.sh https://gist.github.com/BonyChops/fecc50786cb36dc286ebbc3b5c33a3fa/raw/bw-env.sh
  2. Edit ~/scripts/bw.sh

    nano ~/scripts/bw.sh # use your favorite editor
    -export BW_USER='xxx@xxx'
    +export BW_USER='YOUR_ACTUAL_EMAIL@example.com'
    
    # Keychain 上のキー名(ユーザーごとに分けたいならここを工夫)
    export BW_KEY_NAME="BW_SESSION_${BW_USER}"
    
    # ...
  3. Add those on ~/.zshrc

    source ~/scripts/bw.sh
    source ~/scripts/bw-env.sh
    

    And apply it

    source ~/.zshrc
  4. bw prepare

    bw --regenerate-session-key
  5. Done

How to Use

Warning

This process must be performed in a Git-managed directory where the origin remote and its URL are configured. If not configured, create a .bw-env file and write an arbitrary name inside it (this name will be used for the Secure Note name).

  1. cd into the directory where you are using .env.

  2. Use bw-env up to add your .env as a Bitwarden Secure Note.

    $ bw-env up
    Uploading your .env as 📝 env:git@github.com:BonyChops/foobar.git
    Successfully uploaded your .env as 📝 env:git@github.com:BonyChops/foobar.git
    
    Tip: Now you can try run `bw-env show` to test. If it works, you can:
    Tip: rm .env
  3. Confirm with bw-env show.

    You can also confirm this by checking inside the envs folder from the Bitwarden client.

    $ bw-env show
    TEST_SECRET=pass # OK if the expected content is visible
  4. Remove .env after uploading.

    rm .env
  5. When you need to use the variables, just run bw-env.

    $ bw-env
    Loaded environment variables from 📝 env:git@github.com:BonyChops/foobar.git into current shell.
    
    $ node index.js # The secret variables will be applied
    
    $ exit # They will not be reflected in a new shell (you need to run `bw-env` each time)

Footnotes

  1. How to use use Bitwarden CLI with macOS Touch ID

  2. [Biometric unlock for Bitwarden CLI - Feature Requests / Password Manager - Bitwarden Community Forums]

bw-env() {
setopt pipefail
local RED=$'\e[31m'
local GREEN=$'\e[32m'
local RESET=$'\e[0m'
local NOTE_NAME
if [ -f .bw-env ]; then
NOTE_NAME="$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' .bw-env)"
fi
# --- 2. 空なら git remote の origin の最初の URL を使う ---
if [ -z "$NOTE_NAME" ]; then
# git が使える & origin がある時のみ
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
NOTE_NAME="$(git remote get-url --all origin 2>/dev/null | head -n 1)"
fi
fi
# --- 3. どちらも無ければエラー ---
if [ -z "$NOTE_NAME" ]; then
echo "${RED}ERROR: Project should have set up either .bw-env or Git remote 'origin' URL.${RESET}" >&2
return 1
fi
NOTE_NAME="env:$NOTE_NAME"
case "$1" in
'up' )
if [ ! -f .env ]; then
echo "${RED}ERROR: no .env found.${RESET}"
return 1
fi
local FOLDER_NAME="envs"
FOLDER_ID=$(
bw list folders \
| jq -r --arg name "$FOLDER_NAME" '.[] | select(.name==$name).id // empty'
)
if [ -z "$FOLDER_ID" ]; then
if ! FOLDER_ID=$(bw get template folder | jq --arg name $FOLDER_NAME '.name = $name' | bw encode | bw create folder) >/dev/null; then
echo "${RED}ERROR: Creating new folder.${RESET}"
return 1
fi
fi
# 既存アイテムの NOTE_ID を取得(なければ空文字)
local NOTE_ID
NOTE_ID="$(bw get item "$NOTE_NAME" 2>/dev/null | jq -r '.id // empty')"
if [ -z "$NOTE_ID" ]; then
echo "Uploading your .env as 📝 ${NOTE_NAME}"
# ---- なければ create ----
if ! {
bw get template item \
| jq \
--arg notes "$(cat .env)" \
--arg name "$NOTE_NAME" \
--arg folder "$FOLDER_ID" \
'.type = 2
| .secureNote.type = 0
| .notes = $notes
| .name = $name
| .folderId = $folder' \
| bw encode \
| bw create item
} >/dev/null; then
echo "${RED}ERROR: Failed to upload (.env → new item).${RESET}"
echo "Tip: You can try \`bw sync\` then try again."
return 1
fi
echo "${GREEN}Successfully uploaded your .env as 📝 ${NOTE_NAME}.${RESET}"
else
echo "Found existed secure note: ${NOTE_NAME} (${NOTE_ID}), updating..."
# ---- あれば edit ----
if ! {
bw get item "$NOTE_ID" \
| jq \
--arg notes "$(cat .env)" \
--arg name "$NOTE_NAME" \
--arg folder "$FOLDER_ID" \
'.type = 2
| .secureNote.type = 0
| .notes = $notes
| .name = $name
| .folderId = $folder' \
| bw encode \
| bw edit item "$NOTE_ID"
} >/dev/null; then
echo "${RED}ERROR: Failed to update existing item (${NOTE_ID}).${RESET}"
echo "Tip: You can try \`bw sync\` then try again."
return 1
fi
echo "${GREEN}Successfully updated your .env in 📝 ${NOTE_NAME}.${RESET}"
fi
echo
echo "${GREEN}Tip: Now you can try run \`bw-env show\` to test. If it works, you can:"
echo "Tip: rm .env${RESET}"
;;
'show' )
bw get notes "$NOTE_NAME"
;;
* )
# Bitwarden の KEY=VALUE を現在のシェル環境に反映
if ! bw get notes "$NOTE_NAME" >/dev/null; then
echo "${RED}ERROR: Failed to download env from Bitwarden note '${NOTE_NAME}'.${RESET}" >&2
return 1
fi
# 直接 bw の出力を while に流す
while IFS='=' read -r key value; do
[[ -z "$key" ]] && continue # 空行スキップ
[[ "$key" = \#* ]] && continue # コメント行スキップ
# Windows 改行対策(CR除去)
value="${value%$'\r'}"
export "$key=$value"
done < <(bw get notes "$NOTE_NAME" 2>/dev/null)
echo "${GREEN}Loaded environment variables from 📝 ${NOTE_NAME} into current shell.${RESET}"
;;
esac
}
export BW_USER='xxx@xxx'
# Keychain 上のキー名(ユーザーごとに分けたいならここを工夫)
export BW_KEY_NAME="BW_SESSION_${BW_USER}"
bw() {
local -r bw_exec=$(sh -c "which bw")
local -r err_token_not_found="Token not found, please run bw --regenerate-session-key"
_read_token_from_keychain() {
case "$1" in
'--force')
unset bw_session
;;
esac
if [ "$bw_session" = "$err_token_not_found" ]; then
unset bw_session
fi
# 既にシェル変数にあれば再利用
if [ -n "$bw_session" ]; then
return 0
fi
# Keychain から Touch ID 付きで取得
if ! bw_session="$(keymaster get "$BW_KEY_NAME" 2>/dev/null)"; then
bw_session="$err_token_not_found"
echo "$err_token_not_found" >&2
return 1
fi
}
case "$1" in
'--regenerate-session-key')
echo "Regenerating session key, this has invalidated all existing sessions..."
# 既存セッション無効化(サーバ側)
${bw_exec} logout 2>/dev/null || true
# 新しいセッションキーを発行
if ! bw_session="$(${bw_exec} login "${BW_USER}" --raw)"; then
echo "bw login failed" >&2
return 1
fi
if ! keymaster delete "$BW_KEY_NAME"; then
fi
# Keychain に Touch ID 付きで保存
if ! keymaster set "$BW_KEY_NAME" "$bw_session"; then
echo "Failed to store session in keychain via keymaster" >&2
return 1
fi
# シェル内でも即利用
export BW_SESSION="$bw_session"
;;
'login' | 'logout' | 'config' | 'encode')
# login/logout/config は素の bw に丸投げ
${bw_exec} "$@"
;;
'--help' | '-h' | '')
${bw_exec} "$@"
echo "To regenerate your session key type:"
echo " bw --regenerate-session-key"
;;
'get')
case "$2" in
'template')
${bw_exec} "$@"
;;
*)
_read_token_from_keychain || return 1
${bw_exec} "$@" --session "$bw_session"
;;
esac
;;
*)
# それ以外のサブコマンドはセッションキーを噛ませる
_read_token_from_keychain || return 1
${bw_exec} "$@" --session "$bw_session"
;;
esac
}
@BonyChops
Copy link
Author

This threads can be used either Japanese or English :)

@BonyChops
Copy link
Author

現状 bw-env は Secret がそのまま export される仕様になっているから、これも切り替えられた方が安心かなあ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment