VS Codeを使ってDocker container内でRを実行できるようにする

:::note warn
[2024-07-28] もとのDockerfileでインストールされるRのversionが古いことに気付き，それを解決するためにrockerのベースイメージを使うDockerfileに書き直しました．
:::

# やりたいこと
普段，VS CodeのDev Containersを使って，Docker Container内でPythonを利用している．
同じようにVS Codeを使ってDocker Containerの中でRを使えるようにしたい．

# 背景
コンテナ内でRStudioを動かせるようにする方法は（情報源を忘れたが）昔に調べて知っていた[^1]が，RStudioを使うためにブラウザを立ち上げて`localhost:XXXX`と入力するのが面倒だと思っていた．
調べたところVS CodeでRを使うための拡張機能（主要なものは [REditorSupport.r](https://marketplace.visualstudio.com/items?itemName=REditorSupport.r)）があるようだったので，Docker Container内にRをインストールすることにした．

[^1]: 大雑把にいうと，RStudioだけ使えればよければ，rocker（`rocker/tidyverse`とか）を使えばよいのだと思う．

# 解決策：Dockerfileとdevcontainer.jsonの中身
:::note
Dev Container Featuresを利用する解決策もあるそうです。
コメントで情報提供していただいたので、そちらもご覧ください。
自分では試せていませんが、Dev Container Featuresを使う方が手軽かもしれません。
:::
調べた過程を書くのは面倒だし，読み手もそこまで嬉しくないと思うので，いきなり自分がたどり着いた解決策をまとめてしまう．

<details><summary>古い情報（記録のために残します）</summary>

:::note warn
この方法は（少なくとも自分としては）うまくいきません！
具体的には，古めのRがインストールされてしまい，パッケージのインストールの依存関係の処理などで引っかかりました．
このDockerfileの修正では対応しきれなかったので，違うベースイメージを使う方法に変更しました．
:::

方針としては，Pythonのベースイメージを使ってPythonが動く環境をまず用意して，そこに追加でRをインストールする．
Pythonも動くようにするのは，Pythonが[radian](https://github.com/randy3k/radian)というソフトウェア？のrequirementになっているから[^2]．
radianはR用の拡張機能[REditorSupport.r](https://marketplace.visualstudio.com/items?itemName=REditorSupport.r)の説明の中で推奨されていて，正直まだありがたみはわかっていないが，推奨されたものは思考停止でインストールすることにした．

[^2]: あとは，自分がたいていPythonを使うので同じ環境でPythonが動くのは嬉しい，ということもある．

VS CodeのDev Containersを使うので，`Dockerfile`と`devcontainer.json`を`.devcontainer`ディレクトリの中に置くことになる．
それぞれのファイルの中身は次の通り．

```Dockerfile:Dockerfile
# updated on: 2024-07-21
# ----- Part 1: setup Python -----
FROM python:3.12.4-bookworm

# Suppress creation of __pycache__
# value is arbitrary
ENV PYTHONDONTWRITEBYTECODE 1

# for Python Interactive window in VS Code
RUN python -m pip install jupyter==1.0.0  # おそらくここは削っても問題ない

# update setuptools and pip
RUN python -m pip install setuptools==71.0.4
RUN python -m pip install pip==24.1.2

# formatter
RUN python -m pip install black==24.4.2

# additional packages (if any)
#RUN python -m pip install numpy==2.0.0
#RUN python -m pip install matplotlib==3.9.1

# ----- Part 2: setup R -----
# prepare for R installation
RUN apt install -y dirmngr apt-transport-https ca-certificates software-properties-common gnupg2 \
      && gpg --keyserver keyserver.ubuntu.com --recv-key '95C0FAF38DB3CCAD0C080A7BDC78B2DDEABC47B7' \
      && gpg --armor --export '95C0FAF38DB3CCAD0C080A7BDC78B2DDEABC47B7' | \
            tee /etc/apt/trusted.gpg.d/cran_debian_key.asc \
      && add-apt-repository -y 'deb https://cloud.r-project.org/bin/linux/debian bookworm-cran40/'
# install R
RUN apt update && apt install -y r-base

# install libraries
# - requirement & recommendation for R extension for VS Code
RUN R -e "install.packages(c('languageserver', 'httpgd'))"
RUN pip install radian==0.6.12
# - others (if any)
#RUN R -e "install.packages('tidyverse')"
```

```json:devcontainer.json
{
	// A name for the dev container displayed in the UI
	"name": "${localWorkspaceFolderBasename}",

	"build": {
		// Path of the Dockerfile relative to devcontainer.json
		"dockerfile": "Dockerfile"
	},

	// Upon starting, let me know if there is any outdated package.
	"postStartCommand": "python -m pip list --outdated",

	"customizations": {
		// リモート先のVS Codeにインストールする拡張機能
		"vscode": {
			"extensions": [
				// "github.vscode-pull-request-github",
				// Python-related extensions
				"ms-python.python",
				"ms-python.isort",
				"ms-toolsai.jupyter",
				"ms-python.black-formatter",
				// R-related extensions
				"reditorsupport.r",
				"rdebugger.r-debugger"
			],
			"settings": {
				// enable linting using pylint
				"python.linting.enabled": true,
				"python.linting.lintOnSave": true,
				
				// Pylance
				"python.languageServer": "Pylance",
				// enable auto completion for functions
				"python.analysis.completeFunctionParens": true,
		
				// settings for black
				"python.formatting.provider": "black",
				"python.formatting.blackArgs": [
					"--line-length=79"  // 79 or 120?
				],
				"[python]": {
					"editor.defaultFormatter": "ms-python.black-formatter",
					"editor.codeActionsOnSave": {
						// run isort upon saving a file
						"source.organizeImports": true
					},
					"editor.formatOnSave": true
				},
                // R related settings
				"r.rpath.linux": "/bin/R",
				"r.bracketedPaste": true,
				"r.rterm.linux": "/usr/local/bin/radian",
				"r.lsp.path": "/bin/R",
				"r.lsp.debug": true,
				"r.lsp.diagnostics": true,
				"r.rterm.option": [""],
				"r.sessionWatcher": true,
				"r.plot.useHttpgd": true
			}
		}
	}
}
```

コンテナを起動した後にコンソールで`R --version`と入力すれば，Rがインストールされていることは確認できる．

なお，ミニマルにしたければもう少し削ることができると思う．例えばPythonを使う予定が一切なければPython関連の拡張機能などは必要ないだろう．

# 気になっていること
本当はRのバージョンを固定したかったが，それはどうやら難しそう[^3]．

[^3]: https://stackoverflow.com/questions/68486319/installing-a-specific-version-of-r-from-an-apt-repository

# 参考にした記事
ベースイメージとしてDebianベースのものを選んでいるので，DebianにRをインストールする方法を調べた．
最新の情報は出てこなかったので，多少自分で修正してある．
- https://linuxize.com/post/how-to-install-r-on-debian-10/
- https://v-crn.hatenablog.com/entry/2020/12/17/Debian_%E3%81%A7_R_%E3%81%AE%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89
- https://cran.r-project.org/bin/linux/debian/#supported-branches
  - `add-apt-repository -y 'deb https://cloud.r-project.org/bin/linux/debian bookworm-cran40/'`の後ろの部分（`...debian bookworm-cran40/`）は，このサイトを参照して適宜更新する必要がありそう．

</details>

<details open><summary>新しい情報</summary>

方針としては，rockerプロジェクトにあるベースイメージ`r-ver`を使い，rocker_scriptsに含まれている`install_xxx.sh`を使って`radian`を動かすための`python`や，必要な場合は`tidyverse`などをインストールする．
下のDockerfileには，R markdownを使うために`pandoc`をインストールする行もある（ただしコメントアウトしてある）．

`rocker_scripts`に対応するものがなければ，適宜`RUN R -e "install.packages(xxx)"`を追加すればよい．
ただししばしば依存関係でうまくいかないので，先にコンテナの中で試してインストールが必要なものを確認してからDockerfileに追記するとよい．
例えばなにかのパッケージのインストールで`cmake`が必要だと言われたので，そのインストールの行もコメントアウトして追加してある．

```Dockerfile:Dockerfile
# updated on: 2024-07-22
FROM rocker/r-ver:4.4.1
# switch the default CRAN mirror as explained in the web page
RUN echo 'options(repos = c(CRAN = "https://cloud.r-project.org"))' >>"${R_HOME}/etc/Rprofile.site"

# install tidyverse (uncomment if necessary)
#RUN /rocker_scripts/install_tidyverse.sh

# requirement & recommendation for R extension for VS Code
# - install python and then radian
RUN /rocker_scripts/install_python.sh
RUN pip install radian==0.6.12
# - other recommended packages
RUN R -e "install.packages(c('languageserver', 'httpgd'))"

#### uncomment below if necessary ####
# install pandoc for R markdown
#RUN /rocker_scripts/install_pandoc.sh

# install other packages
#RUN apt update && apt install -y cmake
```

また`devcontainer.json`は下のようにした．
R markdownを使うための拡張機能とcsvファイルに色をつける拡張機能が含まれているが，コメントアウトしてある．
Rのパスを記載している部分は，人によっては修正が必要かもしれない（コンテナ内の話なので基本的に共通になると思うが）．

```JSON:devcontainer.json
{
	// A name for the dev container displayed in the UI
	"name": "${localWorkspaceFolderBasename}",

	"build": {
		// Path of the Dockerfile relative to devcontainer.json
		"dockerfile": "Dockerfile"
	},

	"customizations": {
		// リモート先のVS Codeにインストールする拡張機能
		"vscode": {
			"extensions": [
                // extension for handling csv files
				//"mechatroner.rainbow-csv",
				// R-related extensions
				"reditorsupport.r",
				"rdebugger.r-debugger",
				// Live preview for HTML from Rmd
				"ms-vscode.live-server"
			],
			"settings": {
				// R related settings
				"r.rpath.linux": "/usr/local/bin/R",
				"r.bracketedPaste": true,
				"r.rterm.linux": "/usr/local/bin/radian",
				"r.lsp.path": "/usr/local/bin/R",
				"r.lsp.debug": true,
				"r.lsp.diagnostics": true,
				"r.rterm.option": [""],
				"r.sessionWatcher": true,
				"r.plot.useHttpgd": true,
                // related to R markdown
				"files.associations": {
					"*.Rmd": "rmd"
				 }
			}
		}
	}
}
```

</details>