この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
Catalina以降macOSのデフォルトシェルはzshになりましたが、私は慣れ親しんだBashを使っています。
プリインストールされた /bin/bash はありますがバージョンが3.2と古く
かつHomebrewで最新版をインストールするのも万一Homebrew不調時にBashごと動作しなくなってしまったら…という不安があり
PCセットアップのたびBash5.0のソースコードをGNU bashのページからダウンロード/パッチ当て/ビルド/インストールした上でデフォルトシェルの変更をしていました。
先日私用PCでmacOSの再インストールを行う機会があり、
せっかくなのでこの作業をAnsible Playbookで自動化してみました。
動作確認環境
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.6
BuildVersion: 19G73
$ ansible --version
ansible 2.9.10
(中略)
python version = 3.8.3 (default, May 27 2020, 20:54:22) [Clang 11.0.3 (clang-1103.0.32.59)]
構成
フォルダ構成
最低限ローカル端末で動作させるための環境はこんな感じで
全部で3つのyamlファイルを作成しています。
$ tree
.
├── playbook.yml
└── roles
└── bash
├── tasks
│ └── main.yml
└── vars
└── main.yml
ファイルの中身
playbook.yaml
ローカル端末で今回作成したroleを動かす設定を入れます。
---
- hosts: localhost
connection: local
gather_facts: no
become: no
roles:
- bash
roles/以下
vars/main.yml はこんな感じ。
patch_versionsで適用するパッチのバージョン一覧を指定していますが、いい感じの書き方がわからずこんなのになってしまいました。スマートな書き方ご存知でしたらぜひ教えてください…!
---
bash_bin: "/usr/local/bin/bash"
tmp_dir: "/tmp/bash/"
version: 5.0
patch_versions:
- "001"
- "002"
- "003"
- "004"
- "005"
- "006"
- "007"
- "008"
- "009"
- "010"
- "011"
- "012"
- "013"
- "014"
- "015"
- "016"
- "017"
- "018"
tasks/main.yml は以下のような形になりました。
---
# 既存の(非プリインストールの)Bash存在チェック
- name: bash existing check
stat:
path: "{{ bash_bin }}"
register: result
- block:
# 作業用ディレクトリ作成
- name: create tmp dir
file:
path: "{{ tmp_dir }}"
state: directory
mode: 0755
# ソースコードのダウンロード・展開
- name: download bash {{ version }}
get_url:
url: "http://ftp.gnu.org/gnu/bash/bash-{{version}}.tar.gz"
dest: "{{ tmp_dir }}/bash-{{version}}.tar.gz"
- name: unarchive bash {{ version }}
command:
cmd: "tar -zxvf {{ tmp_dir }}/bash-{{version}}.tar.gz -C {{ tmp_dir }}/"
# パッチ適用
- block:
- name: download patches
get_url:
url: "http://ftp.gnu.org/gnu/bash/bash-{{version}}-patches/bash50-{{ item }}"
dest: "{{ tmp_dir }}/bash-{{ version }}"
with_items: "{{ patch_versions }}"
- name: apply patches
patch:
src: "{{ tmp_dir }}/bash-{{ version }}/bash50-{{ item }}"
basedir: "{{ tmp_dir }}/bash-{{ version }}"
with_items: "{{ patch_versions }}"
when: patch_versions is defined
# インストール
- name: configure
command:
chdir: "{{ tmp_dir }}/bash-{{ version }}"
cmd: ./configure
- name: make
make:
chdir: "{{ tmp_dir }}/bash-{{ version }}"
- name: make install
make:
chdir: "{{ tmp_dir }}/bash-{{ version }}"
target: install
become: yes
# /etc/shells編集
- name: edit /etc/shells
lineinfile:
dest: /etc/shells
line: "{{ bash_bin }}"
become: yes
# デフォルトシェルの変更
- name: chsh
command:
"chsh -s {{ bash_bin }}"
# 作業用ファイル削除
- name: remove tmp dir
file:
state: absent
path: "{{ tmp_dir }}/"
when: result.stat.exists == False
ソースコード展開時にはunarchiveモジュールを使いたかったのですが、
unarchiveモジュールはmacにデフォルトで入っているBSD tarではなくGNU tarの存在を前提にしている様子で、私の環境では動作しませんでした。
実行
-Kオプションにより、最初にsudo用のパスワードを要求されます。あと、chsh実行時にもユーザのパスワードが必要です。
ok, changedの数は状況に応じて変わると思います。
Warningも赤裸々に見せちゃうぜ
$ ansible-playbook playbook.yml -K
BECOME password:
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] **********************************************************************************************************************************
TASK [\bash existing check] ************************************************************************************************************************
ok: [localhost]
TASK [\bash : create tmp dir] **********************************************************************************************************************
ok: [localhost]
TASK [download bash 5.0] **************************************************************************************************************************
ok: [localhost]
TASK [unarchive bash 5.0] *************************************************************************************************************************
[WARNING]: Consider using the unarchive module rather than running 'tar'. If you need to use command because unarchive is insufficient you can
add 'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.
changed: [localhost]
TASK [\bash : download patches] ********************************************************************************************************************
ok: [localhost] => (item=001)
ok: [localhost] => (item=002)
ok: [localhost] => (item=003)
ok: [localhost] => (item=004)
ok: [localhost] => (item=005)
ok: [localhost] => (item=006)
ok: [localhost] => (item=007)
ok: [localhost] => (item=008)
ok: [localhost] => (item=009)
ok: [localhost] => (item=010)
ok: [localhost] => (item=011)
ok: [localhost] => (item=012)
ok: [localhost] => (item=013)
ok: [localhost] => (item=014)
ok: [localhost] => (item=015)
ok: [localhost] => (item=016)
ok: [localhost] => (item=017)
ok: [localhost] => (item=018)
TASK [\bash : apply patches] ***********************************************************************************************************************
changed: [localhost] => (item=001)
changed: [localhost] => (item=002)
changed: [localhost] => (item=003)
changed: [localhost] => (item=004)
changed: [localhost] => (item=005)
changed: [localhost] => (item=006)
changed: [localhost] => (item=007)
changed: [localhost] => (item=008)
changed: [localhost] => (item=009)
changed: [localhost] => (item=010)
changed: [localhost] => (item=011)
changed: [localhost] => (item=012)
changed: [localhost] => (item=013)
changed: [localhost] => (item=014)
changed: [localhost] => (item=015)
changed: [localhost] => (item=016)
changed: [localhost] => (item=017)
changed: [localhost] => (item=018)
TASK [\bash : configure] ***************************************************************************************************************************
changed: [localhost]
TASK [\bash : make] ********************************************************************************************************************************
changed: [localhost]
TASK [\bash : make install] ************************************************************************************************************************
changed: [localhost]
TASK [\bash : edit /etc/shells] ********************************************************************************************************************
ok: [localhost]
TASK [\bash : chsh] ********************************************************************************************************************************
Password for kato.saori:
changed: [localhost]
TASK [\bash : remove tmp dir] **********************************************************************************************************************
changed: [localhost]
PLAY RECAP ****************************************************************************************************************************************
localhost : ok=12 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
シェルを読み込み直すと、パッチ適用された最新のBashが動いてることの確認がとれますぞ!
$ bash --version
GNU bash, バージョン 5.0.18(2)-release (x86_64-apple-darwin19.6.0)
Copyright (C) 2019 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
おわりに
これで今後のmacのセットアップが楽になるので嬉しいです!