Skip to content

Commit

Permalink
Merge pull request #2 from erichlf/DevcontainerExec
Browse files Browse the repository at this point in the history
Add DevcontainerExec and refactor for DRYness
  • Loading branch information
erichlf authored Apr 24, 2024
2 parents 68c5df9 + 7d26395 commit cf193b2
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 200 deletions.
151 changes: 102 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,86 @@
# Devcontainer CLI (Nvim Plugin)

Develop your next Repo in a Devcontainer using Nvim thanks to the [Devconatiner CLI](https://github.com/devcontainers/cli) and this plugin
Develop your next Repo in a Devcontainer using Nvim thanks to the
[Devconatiner CLI](https://github.com/devcontainers/cli) and this plugin
![](doc/gifs/nvim_devcontainer_cli-description.gif)

As you can see in the GIF above, [alacritty](https://github.com/alacritty/alacritty) is being used as a Terminal Emulator. Any of the ones recommended [here](https://www.lazyvim.org/) would work. In case you are struggling configuring the nerd fonts, I personally recommend this great [youtube video](https://www.youtube.com/watch?v=mQdB_kHyZn8&t=182s).
As you can see in the GIF above,
[alacritty](https://github.com/alacritty/alacritty) is being used as a Terminal
Emulator. Any of the ones recommended [here](https://www.lazyvim.org/) would
work. For dotfiles setup I would recommend looking at the `devcontainer` branch
of [my dotfiles](https://github.com/erichlf/dotfiles). The `install.sh` script is
quite simple, but should be very informative.

---

First, which problem is this plugin trying to solve?
First, what problem is this plugin trying to solve?

**Situation:**

Your favorite editor is **nvim** and you are currently developing a containerized application (using Docker).
Your favorite editor is **nvim** and you are currently developing a
containerized application (using Docker).

**Problem:**

You can definitely use nvim for developing your code, but you quickly face problems with the [LSP](https://microsoft.github.io/language-server-protocol/) and the [DAP](https://microsoft.github.io/debug-adapter-protocol/) (among other plugins), because such plugins do not have access inside the Docker container. True, you can install **nvim** together with all your plugins inside the docker container extending the image. However, this can be cumbersome, and ultimately if you are working in a team, chances are you are the only one is using **nvim**. Also, you do not want to modify your own Docker Target inside your Dockerfile, installing **nvim** etc.
Your team is using a devcontainer (or a docker container) and you want to still
use **nvim** with [LSP](https://microsoft.github.io/language-server-protocol/)
and [DAP](https://microsoft.github.io/debug-adapter-protocol/) (among other
plugins), but you don't want to have to run all the cumbersome commands.

**Solution:**

There are multiple IDEs out there who give you the possibility to execute themself inside the Docker container you are developing, fixing the problems above, but there is nothing which works out-of-the-box for **nvim**. Recently, Microsoft opened the code used in VSCode for attaching the IDE to such containers ([Devconatiner CLI](https://github.com/devcontainers/cli)).
There are multiple IDEs out there who give you the possibility to execute
themself inside the Docker container you are developing, fixing the problems
above, but there is nothing which works out-of-the-box for **nvim**. Recently,
Microsoft opened the command line tool,
([Devconatiner CLI](https://github.com/devcontainers/cli)), which allows developers
to run devcontainers without VScode.

The current **nvim** plugin aims to take advantage of such CLI for creating your own local development environment on the top of your containerized applications. This plugin allows you use LSP capabilities for external modules (installed inside the Docker container), and also debug your application ([DAP](https://microsoft.github.io/debug-adapter-protocol/)).
The current **nvim** plugin aims to take advantage of `devcontainer-cli` for
creating your own local development environment on top of a containerized
applications. This plugin allows you use LSP capabilities for external modules
(installed inside the Docker container), and also debug your application
([DAP](https://microsoft.github.io/debug-adapter-protocol/)).

But, what is happening under the hood?

1. First, devcontainer-cli is used for setting up your devcontainer, building the image based on the instructions defined in your [devcontainer.json](.devcontainer/devcontainer.json) and initializing a container based on such image.
1. Once the container is already running, nvim installed inside the Docker container together with a set of dependencies that can be found [here](https://github.com/arnaupv/nvim-devcontainer-cli/blob/main/bin/devcontainer_setup_scripts/root_setup.sh). This step resembles the installation of the [vscode-server](https://code.visualstudio.com/docs/devcontainers/containers) inside the container when using VSCode.
1. Finally, nvim needs certain configuration to work properly. That's why the following [nvim_dotfiles_repo](https://github.com/arnaupv/dotfiles) is cloned inside the container ([here](https://github.com/arnaupv/nvim-devcontainer-cli/blob/main/bin/devcontainer_setup_scripts/none_root_setup.sh#L6)).
1. The last step is connecting inside the container. This could be done by `ssh` connection, but in this case the connection is done using `devcontainer exec` ([here](https://github.com/arnaupv/nvim-devcontainer-cli/blob/main/bin/connect_to_devcontainer.sh)).

As you can see what the plugin does is installing and configuring neovim inside the container, instead of communicating with the info inside the container via nvim client/server. One of the negative consequences of such approach is that all plugins need to be installed each time a devcontainer session starts. This is far from being efficient and it is something that needs to be improved in the future. However, I personally consider that the current solution is good enough for starting to work with nvim inside a Docker container.
1. First, `devcontainer-cli` is used for setting up your devcontainer, building
the image based on the instructions defined in your
[devcontainer.json](.devcontainer/devcontainer.json) and initializing a
container based on such image.
2. Once the container is running, your dotfiles are installed in the docker
container together with a set of dependencies. To install any dependencies you need either the
dotfiles setup script will need to do that or you can use devcontainer features to install them.
A very nice devcontainer feature that can do this is
[apt package](https://github.com/rocker-org/devcontainer-features/tree/main/src/apt-packages).
3. The last step is connecting inside the container via `devcontainer exec`
([here](https://github.com/erichlf/nvim-devcontainer-cli/blob/main/bin/connect_to_devcontainer.sh)).

The main thing this plugin does is bringup your devcontainer and execute
commands via a convenient interface. It attempts to stay out of your way and
allows you to do things as you wish, but gives you the tools to do that easily.

**Inspiration:**

This plugin has been inspired by the work previously done by [esensar](https://github.com/esensar/nvim-dev-container) and by [jamestthompson3](https://github.com/jamestthompson3/nvim-remote-containers). The main different is that this plugin benefits from the [Devcontainer CLI](https://github.com/devcontainers/cli) which was opensourced by Microsoft in April 2022.
This plugin has been inspired by the work previously done by
[arnaupv](https://github.com/arnaupv/nvim-devcontainer-cli),
[esensar](https://github.com/esensar/nvim-dev-container) and by
[jamestthompson3](https://github.com/jamestthompson3/nvim-remote-containers).
The main difference between this version and arnaupv is that it tries to not
make assumptions about how you work.

# Dependencies

- Only Ubuntu/Debian Docker Host (tested with Ubuntu 20.04 and 22.04) - [Planning to support other OS in the future](https://github.com/arnaupv/nvim-devcontainer-cli/issues/5).
- [docker](https://docs.docker.com/get-docker/)
- [devcontainer-cli](https://github.com/devcontainers/cli#npm-install)
- [jq](https://jqlang.github.io/jq/download/)

# 🔧 Installation

- [lazy.nvim](https://github.com/folke/lazy.nvim)

```lua
{
"arnaupv/nvim-devcontainer-cli",
"erichlf/nvim-devcontainer-cli",
opts = {
-- whather to verify that the final devcontainer should be run
interactive = false,
Expand All @@ -60,26 +91,39 @@ This plugin has been inspired by the work previously done by [esensar](https://g
remove_existing_container = true,
-- By default, if no extra config is added, following nvim_dotfiles are
-- installed: "https://github.com/LazyVim/starter"
-- This is an example for configuring other nvim_dotfiles inside the docker container
nvim_dotfiles_repo = "https://github.com/arnaupv/dotfiles.git",
nvim_dotfiles_branch = "main", -- branch to clone from nvim_dotfiles_repo
nvim_dotfiles_install_command = "cd ~/nvim_dotfiles/ && ./install.sh",
-- In case you want to change the way the devenvironment is setup, you can also provide your own setup
setup_environment_repo = "https://github.com/arnaupv/setup-environment",
setup_environment_install_command = "./install.sh -p 'nvim stow zsh'",
-- This is an example for configuring other dotfiles inside the docker container
dotfiles_repository = "https://github.com/erichlf/dotfiles.git",
dotfiles_branch = "main", -- branch to clone from dotfiles_repository`
dotfiles_targetPath = "~/dotfiles", -- location to install dotfiles
dotfiles_intallCommand = "install.sh", -- script to run after dotfiles are cloned
},
keys = {
-- stylua: ignore
{
"<leader>cdu",
"<leader>Du",
":DevcontainerUp<cr>",
desc = "Up the DevContainer",
desc = "Bring up the DevContainer",
},
{
"<leader>cdc",
"<leader>Dc",
":DevcontainerConnect<cr>",
desc = "Connect to DevContainer",
},
{
"<leader>De",
":DevcontainerExec<cr>",
desc = "Execute a command in DevContainer",
},
{
"<leader>Db",
":DevcontainerExec cd build && make<cr>",
desc = "Execute build command in DevContainer",
},
{
"<leader>Dt",
":DevcontainerExec cd build && make test<cr>",
desc = "Execute test command in DevContainer",
},
}
},
```
Expand All @@ -88,29 +132,41 @@ The default_config can be found [here](./lua/devcontainer_cli/config/init.lua).

# How to use?

There are 2 commands: `:DevcontainerUp` and `:DevcontainerConnect`.

1. First you need to have your folder with the devcontainer instructions. This folder is usually called `.devcontainer` and contains a `devcontainer.json` file. This file is used by the [Devcontainer CLI](https://github.com/devcontainers/cli). As a first approach you can copy-paste the [.devcontainer](.devcontainer/devcontainer.json) folder of the current project and adapt it for your repo. You can also find more information about the `devcontainer.json` file [here](https://code.visualstudio.com/docs/remote/devcontainerjson-reference).

1. Then open a nvim session and execute the first command: `DevcontainerUp`, which will create the image based on your Dockerfile. Once created it will initialize a container with the previously created image, adding nvim and other tools defined in ./bin/devcontainer_setup_scripts/ . Currently the following [dotfiles](https://github.com/arnaupv/dotfiles) are hardcoded [here](./bin/devcontainer_setup_scripts/none_root_setup.sh). The new devcontainer running can be easily checked with the following command: `docker ps -a`.

1. If the process above finishes successfully, you are prepared for closing the current nvim session and open a new nvim inside the docker container. All this can be done from nvim itself, using the second command: `:DevcontainerConnect`.

1. As an example, you can try to create the first devcontainer using cloning the current repository, following the instructions above.
There are 3 main commands: `:DevcontainerUp`, `:DevcontainerExec`, and `:DevcontainerConnect`.

1. First, you should be in the main direcotry or subdirectory of your project
container you `.devcontainer` directory. This file is used by the
[Devcontainer CLI](https://github.com/devcontainers/cli). As a first
approach you can copy-paste the
[.devcontainer](.devcontainer/devcontainer.json) folder of the current
project and adapt it for your repo. You can also find more information about
the `devcontainer.json` file
[here](https://code.visualstudio.com/docs/remote/devcontainerjson-reference).
2. Then open a **nvim** session and execute the first command:
`DevcontainerUp`, which will create the image based on your
`.devcontainer\devcontainer.json`. Once created it will initialize a
container with the previously created image, and then clone your dotfiles,
and finally run the specified setup script. The new devcontainer running can
be easily checked with the following command: `docker ps -a`.
3. If the process above finishes successfully, you can choose to close the
current **nvim** session and open a new session within the devcontainer via
the command: `:DevcontainerConnect`. Alternatively, you could choose to
continue working in your current session and run commands in the
devcontainer via `DevcontainerExec`.

# Tests

Tests are executed automatically on each PR using Github Actions.

In case you want to run Github Actions locally, it is recommended to use [act](https://github.com/nektos/act#installation).
And then execute:
In case you want to run Github Actions locally, it is recommended to use
[act](https://github.com/nektos/act#installation). And then execute:

```bash
act -W .github/workflows/default.yml
```

Another option would be to connect to the devcontainer following the **How to use?** section.
Once connected to the devcontainer, execute:
Another option would be to connect to the devcontainer following the **How to
use?** section. Once connected to the devcontainer, execute:

```bash
make test
Expand All @@ -119,12 +175,9 @@ make test
# FEATUREs (in order of priority)

1. [x] Capability to create and run a devcontainer using the [Devconatiner CLI](https://github.com/devcontainers/cli).
1. [x] Capability to attach in a running devcontainer
1. [x] The floating window created during the devcontainer Up process (:DevcontainerUp<cr>) is closed when the process finishes successfully.
1. [x] [Give the possibility of defining custom dotfiles when setting up the devcontainer](https://github.com/arnaupv/nvim-devcontainer-cli/issues/1)
1. [x] [Detect the cause/s of the UI issues of neovim when running inside the docker container.](https://github.com/arnaupv/nvim-devcontainer-cli/issues/15)
1. [ ] Add unit tests using plenary.busted lua module.
1. [ ] The logs printed in the floating window when preparing the Devcontainer are saved and easy to access.

1. [ ] Convert bash scripts in lua code.
1. [ ] Create .devcontainer/devcontainer.json template automatically via a nvim command. Add examples for when the devcontainer is created from docker and also from docker-compose.
2. [x] Capability to attach in a running devcontainer.
3. [x] The floating window created during the devcontainer Up process (:DevcontainerUp<cr>) is closed when the process finishes successfully.
4. [x] [Give the possibility of defining custom dotfiles when setting up the devcontainer](https://github.com/erichlf/nvim-devcontainer-cli/issues/1)
5. [ ] Add unit tests using plenary.busted lua module.
6. [ ] The logs printed in the floating window when preparing the Devcontainer are saved and easy to access.
7. [ ] Convert bash scripts in lua code.
8 changes: 5 additions & 3 deletions doc/devcontainer_cli.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ developing docker containers.
Development is in progress, but the plugin can already be used.

To find out more:
https://github.com/arnaupv/nvim-devcontainer-cli
https://github.com/erichlf/nvim-devcontainer-cli

:h DevcontainerUp

DevcontainerUp *DevcontainerUp*
Spawns a docker devcontainer, installing neovim and all the required
dependencies for developing your code inside the docker container.
Spawns a devcontainer, installing dotfiles in the docker container.

DevcontainerExec *DevcontainerExec*
Runs a given command in the projects devcontainer.

DevcontainerConnect *DevcontainerConnect*
Closes the nvim sessions (all sessions fromt the terminal) and opens a new
Expand Down
19 changes: 7 additions & 12 deletions lua/devcontainer_cli/config/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,15 @@ local default_config = {
-- If set to True [default_value] it can take extra time as you force to start from scratch
remove_existing_container = true,
-- dependencies that have to be installed in the devcontainer (remoteUser = root)
setup_environment_repo = "https://github.com/arnaupv/setup-environment",
dotfiles_repository = "[email protected]:erichlf/dotfiles",
-- branch to checkout for repositories (this is a feature not supported by devcontainers in general, but we do)
dotfiles_repository = "devcontainer",
-- directory for the setup environment
setup_environment_directory = "setup_dotfiles",
dotfiles_targetPath = "~/dotfiles",
-- command that's executed for installed the dependencies from the setup_environment_repo
setup_environment_install_command = "install.sh",

-- nvim_dotfiles that will be installed inside the docker devcontainer through the devcontainer cli.
nvim_dotfiles_repo = "https://github.com/LazyVim/starter",
-- branch to use for the nvim_dotfiles
nvim_dotfiles_branch = "main",
-- directory where to put the nvim_dotfiles
nvim_dotfiles_directory = "nvim_dotfiles",
-- nvim_dotfiles_install is the command that needs to be executed to install the dotfiles (it can be any bash command)
nvim_dotfiles_install_command = "mkdir -p ~/.config && mv ~/nvim_dotfiles ~/.config/nvim",
dotfiles_installCommand = "install.sh",
-- The number of columns to wrap text at
terminal_columns = 80,
}

local options
Expand Down
26 changes: 14 additions & 12 deletions lua/devcontainer_cli/devcontainer_cli.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
local config = require("devcontainer_cli.config")
local folder_utils = require("devcontainer_cli.folder_utils")
local devcontainer_utils = require("devcontainer_cli.devcontainer_utils")

local M = {}
Expand All @@ -20,19 +19,22 @@ local function define_autocommands()
})
end

function M.up()
-- bringup the devcontainer
devcontainer_parent = folder_utils.get_root(config.toplevel)
if devcontainer_parent == nil then
prev_win = vim.api.nvim_get_current_win()
vim.notify(
"Devcontainer folder not available. devconatiner_cli_plugin plugin cannot be used",
vim.log.levels.ERROR
)
return
-- executes a given command in the devcontainer of the current project directory
-- @param opts options for executing the command
function M.exec(opts)
cwd = vim.loop.cwd()

vim.validate({ args = { opts.args, "string" } })
if opts.args == nil or opts.args == "" then
devcontainer_utils.exec(cwd)
else
devcontainer_utils.exec_cmd(opts.args, cwd)
end
end

devcontainer_utils.bringup(devcontainer_parent)
-- bring up the devcontainer in the current project directory
function M.up()
devcontainer_utils.bringup(vim.loop.cwd())
end

function M.connect()
Expand Down
Loading

0 comments on commit cf193b2

Please sign in to comment.