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
22 changes: 22 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ Example enabling more than one::
travisfile2dockerfile --build-env-args VIM_INSTALL ZSH_INSTALL \
git@github.com:Vauxoo/forecast.git 8.0

VS Code support (``DEPLOYV_VSCODE``)
====================================

Opt-in with the environment variable ``DEPLOYV_VSCODE=1``::

DEPLOYV_VSCODE=1 travisfile2dockerfile git@github.com:Vauxoo/forecast.git 8.0

It is disabled by default so vim/terminal users do not pay the extra build
time. When enabled:

- The Dockerfile pre-installs the VS Code server and the extensions listed in
``templates/.vscode/extensions.json`` at **build** time, so attaching VS Code
to the container does not download anything live. The server version is
pinned to the commit of your local ``code`` binary when available (run
``travisfile2dockerfile`` again after upgrading VS Code to re-pin it);
otherwise the latest stable server is used. If the pinned server does not
match your client, VS Code just falls back to downloading its own version;
the pre-installed extensions are version-independent and are reused anyway.
- A ``.devcontainer.json`` is generated next to the Dockerfile pointing to the
image built by ``10-build.sh``, so opening that folder in VS Code offers
"Reopen in Container" automatically.

codebase-memory-deployv
=======================

Expand Down
5 changes: 5 additions & 0 deletions src/travis2docker/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,13 +284,18 @@ def main(return_result=False):
os_kwargs.update({"remotes": remotes, "git_base": git_base})
if docker_user:
os_kwargs.update({"user": docker_user})
# Opt-in with DEPLOYV_VSCODE=1: pre-install the VS Code server and extensions in the
# image and generate a .devcontainer.json. Disabled by default so e.g. vim users
# do not pay the extra build time downloading VS Code stuff they will never use
vscode = os.environ.get("DEPLOYV_VSCODE", "").strip().lower() not in ("", "0", "false", "no")
t2d = Travis2Docker(
work_path=pathlib.Path(root_path) / "script" / GitRun.url2dirname(git_repo) / revision,
image=default_docker_image,
os_kwargs=os_kwargs,
copy_paths=[(pathlib.Path("~/.ssh").expanduser(), "$HOME/.ssh")] + rcfiles,
build_env_args=build_env_args,
build_extra_steps=args.build_extra_steps,
vscode=vscode,
)
t2d.build_extra_params = {
"extra_params": build_extra_args,
Expand Down
18 changes: 18 additions & 0 deletions src/travis2docker/templates/Dockerfile_deployv
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ RUN . /home/odoo/build.sh && \
[ -e /entrypoint ] && mv /entrypoint /deployv_entrypoint ; \
mkdir -p /run/sshd

{% if vscode %}
# DEPLOYV_VSCODE: pre-install the VS Code server and extensions at build time
# so attaching VS Code to the container does not download anything live
RUN arch=$(uname -m) && case "$arch" in x86_64) arch=x64 ;; aarch64|arm64) arch=arm64 ;; esac && \
tmp_dir=$(mktemp -d) && \
curl -fsSL "https://update.code.visualstudio.com/{{ vscode_version }}/server-linux-$arch/stable" \
| tar -xz -C "$tmp_dir" --strip-components=1 && \
commit=$(sed -n 's/.*"commit": *"\([^"]*\)".*/\1/p' "$tmp_dir/product.json") && \
mkdir -p /home/odoo/.vscode-server/bin && \
mv "$tmp_dir" "/home/odoo/.vscode-server/bin/$commit" && \
"/home/odoo/.vscode-server/bin/$commit/bin/code-server" \
--extensions-dir /home/odoo/.vscode-server/extensions \
--user-data-dir /home/odoo/.vscode-server/data \
{%- for extension in vscode_extensions %}
--install-extension {{ extension }} \
{%- endfor %}
&& chown -R {{ user }}:{{ user }} /home/odoo/.vscode-server
{% endif %}
{% for step in build_extra_steps %}
RUN {{ step }}
{% endfor %}
Expand Down
14 changes: 14 additions & 0 deletions src/travis2docker/templates/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "{{ name }}",
"image": "{{ image }}",
"remoteUser": "{{ user }}",
"workspaceFolder": "/home/odoo/instance",
"customizations": {
"vscode": {
"extensions": {{ vscode_extensions | tojson }},
"settings": {
"python.analysis.extraPaths": ["/home/odoo/instance/odoo"]
}
}
}
}
34 changes: 34 additions & 0 deletions src/travis2docker/travis2docker.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# pylint: disable=useless-object-inheritance,consider-using-with
import json
import logging
import pathlib
import re
import shutil
import stat
import subprocess
from tempfile import gettempdir

import jinja2
Expand Down Expand Up @@ -42,6 +44,20 @@ def run_template(self):
def chmod_execution(file_path):
file_path.chmod(file_path.stat().st_mode | stat.S_IEXEC)

@staticmethod
def get_vscode_version():
"""Return "commit:SHA" of the local VS Code client to pre-install the matching
server into the image, or "latest" if there is no "code" binary available"""
code_bin = shutil.which("code")
if code_bin:
try:
lines = subprocess.check_output([code_bin, "--version"]).decode("UTF-8").splitlines()
if len(lines) >= 2 and lines[1].strip():
return "commit:%s" % lines[1].strip()
except (subprocess.CalledProcessError, OSError):
pass
return "latest"

def __init__(
self,
image=None,
Expand All @@ -52,8 +68,10 @@ def __init__(
copy_paths=None,
build_env_args=None,
build_extra_steps=None,
vscode=False,
):
self.curr_work_path = None
self.vscode = vscode
self.build_extra_params = {}
self.run_extra_params = {}
self.build_env_args = build_env_args
Expand All @@ -76,6 +94,10 @@ def __init__(
copy_paths.append([templates_dir / ".vscode", "/home/odoo/.vscode"])
copy_paths.append([templates_dir / ".coveragerc", "/home/odoo/.coveragerc"])
os_kwargs.setdefault("user", "odoo")
if self.vscode:
extensions_data = json.loads((templates_dir / ".vscode" / "extensions.json").read_text())
os_kwargs.setdefault("vscode_extensions", extensions_data["recommendations"])
os_kwargs.setdefault("vscode_version", self.get_vscode_version())
if dockerfile is None:
dockerfile = "Dockerfile"
if templates_path is None:
Expand Down Expand Up @@ -116,16 +138,28 @@ def compute_dockerfile(self):
"image": self.image,
"build_env_args": self.build_env_args,
"build_extra_steps": self.build_extra_steps,
"vscode": self.vscode,
}
kwargs.update(self.os_kwargs)
with curr_dockerfile.open("w") as f_dockerfile:
dockerfile_content = self.dockerfile_template.render(kwargs).strip("\n ")
f_dockerfile.write(dockerfile_content)
if self.vscode:
self.compute_devcontainer()
self.compute_build_scripts()
work_paths = [str(self.curr_work_path)]
self.curr_work_path = None
return work_paths

def compute_devcontainer(self):
"""Generate a .devcontainer.json so VS Code offers reopening the generated
image as a Development Container"""
name = self.os_kwargs.get("repo_project") or self.variables_sh_data.get("main_app", "odoo")
devcontainer_content = self.jinja_env.get_template("devcontainer.json").render(
image=self.new_image, name=name, **self.os_kwargs
)
(self.curr_work_path / ".devcontainer.json").write_text(devcontainer_content)

def copy_path(self, path):
""":param paths list: List of paths to copy"""
src = pathlib.Path(path).expanduser()
Expand Down
46 changes: 46 additions & 0 deletions tests/test_travis2docker.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# pylint: disable=consider-using-with

import json
import os
import pathlib
import subprocess
Expand Down Expand Up @@ -104,6 +105,51 @@ def test_main_deployv(tmp_path, monkeypatch):
check_dockerfile_lint(scripts)


def test_main_deployv_vscode(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("DEPLOYV_VSCODE", "1")
repo = create_repo(tmp_path, {"variables.sh": VARIABLES_SH})
sys.argv = [
"travis2docker",
repo,
"main",
"--root-path",
str(tmp_path / "t2d"),
]
scripts = cli_main(return_result=True)
assert len(scripts) == 1, "Scripts returned should be 1"
work_path = pathlib.Path(scripts[0])
dkr_content = (work_path / "Dockerfile").read_text()
assert "update.code.visualstudio.com" in dkr_content
assert "/home/odoo/.vscode-server" in dkr_content
assert "--install-extension ms-python.python" in dkr_content
devcontainer = json.loads((work_path / ".devcontainer.json").read_text())
assert devcontainer["name"] == "myproject"
assert devcontainer["remoteUser"] == "odoo"
assert devcontainer["workspaceFolder"] == "/home/odoo/instance"
assert devcontainer["image"].endswith(":main")
assert "ms-python.python" in devcontainer["customizations"]["vscode"]["extensions"]
check_dockerfile_lint(scripts)


def test_main_deployv_without_vscode(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
monkeypatch.delenv("DEPLOYV_VSCODE", raising=False)
repo = create_repo(tmp_path, {"variables.sh": VARIABLES_SH})
sys.argv = [
"travis2docker",
repo,
"main",
"--root-path",
str(tmp_path / "t2d"),
]
scripts = cli_main(return_result=True)
work_path = pathlib.Path(scripts[0])
dkr_content = (work_path / "Dockerfile").read_text()
assert ".vscode-server" not in dkr_content
assert not (work_path / ".devcontainer.json").exists()


def test_main_docker_image_parameter(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
repo = create_repo(tmp_path, {"variables.sh": VARIABLES_SH})
Expand Down
Loading