Skip to content

Commit

Permalink
Danielbraun89/issue16 (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbraun89 authored Mar 17, 2023
1 parent f40f9bb commit cc9b6ee
Show file tree
Hide file tree
Showing 17 changed files with 352 additions and 85 deletions.
43 changes: 29 additions & 14 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,28 @@
"console": "integratedTerminal",
"justMyCode": false
},

{
"name": "debug feature download",
"name": "debug --release-version",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/dcontainer/__main__.py",
"console": "integratedTerminal",
"justMyCode": false,
"args": [
"feature",
"download",
"ghcr.io/lukewiwa/features/shellcheck:0",
]
"--release-version",

]
},
{
"name": "debug --release-version",
"name": "debug --help",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/dcontainer/__main__.py",
"console": "integratedTerminal",
"justMyCode": false,
"args": [
"--release-version",
"--help",

]
},
Expand All @@ -58,26 +57,42 @@
"console": "integratedTerminal",
"justMyCode": false,
"args": [
"feature",
"install",
"devcontainer-feature",
"ghcr.io/devcontainers-contrib/features/bash-command:1.0.0",
"--option",
"command=\"pip3 install packaging==21.3\""
]
},
{
"name": "debug feature inspect",
"name": "debug apt-get install",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/dcontainer/__main__.py",
"console": "integratedTerminal",
"justMyCode": false,
"args": [
"install",
"apt-get",
"wget",
]
},
{
"name": "debug apt-get install on ubuntu",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/dcontainer/__main__.py",
"console": "integratedTerminal",
"justMyCode": false,
"args": [
"feature",
"inspect",
"ghcr.io/ebaskoro/devcontainer-features/sdkman:1.0.0",
]
"install",
"apt-get",
"wget",
"--ppa",
"ppa:deadsnakes/ppa",
]
},

{
"name": "Debug Tests",
"type": "python",
Expand Down
2 changes: 1 addition & 1 deletion dcontainer/__main__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import typer

from dcontainer.cli.install import app as install_app
from dcontainer.cli.generate import app as generate_app
from dcontainer.cli.install import app as install_app
from dcontainer.utils.version import (
resolve_own_package_version,
resolve_own_release_version,
Expand Down
Empty file added dcontainer/apt_get/__init__.py
Empty file.
88 changes: 88 additions & 0 deletions dcontainer/apt_get/apt_get_installer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import platform
from typing import List, Optional

from dcontainer.utils.sudo_invoker import SudoInvoker


class AptGetInstaller:
class PPASOnNonUbuntu(Exception):
pass

class AptGetUpdateFailed(SudoInvoker.SudoInvokerException):
pass

class AddPPAsFailed(SudoInvoker.SudoInvokerException):
pass

class RemovePPAsFailed(SudoInvoker.SudoInvokerException):
pass

class CleanUpFailed(SudoInvoker.SudoInvokerException):
pass

@staticmethod
def normalize_ppas(ppas: List[str]) -> List[str]:
# normalize ppas to have the ppa: initials
for ppa_idx, ppa in enumerate(ppas):
if "ppa:" != ppa[:4]:
ppas[ppa_idx] = f"ppa:{ppa}"
return ppas

@staticmethod
def install(
packages: List[str],
ppas: Optional[List[str]] = None,
force_ppas_on_non_ubuntu: bool = True,
remove_ppas_on_completion: bool = True,
remove_cache_on_completion: bool = True,
) -> None:
if (
ppas
and not "ubuntu" in platform.version().lower()
and not force_ppas_on_non_ubuntu
):
raise AptGetInstaller.PPASOnNonUbuntu()

normalized_ppas = AptGetInstaller.normalize_ppas(ppas)

try:
SudoInvoker.invoke(
command="apt-get update -y",
exception_class=AptGetInstaller.AptGetUpdateFailed,
)

if ppas:
SudoInvoker.invoke(
command="apt-get install -y software-properties-common",
exception_class=AptGetInstaller.AddPPAsFailed,
)

for ppa in normalized_ppas:
SudoInvoker.invoke(
command=f"add-apt-repository -y {ppa}",
exception_class=AptGetInstaller.AddPPAsFailed,
)

SudoInvoker.invoke(
command="apt-get update -y",
exception_class=AptGetInstaller.AptGetUpdateFailed,
)

SudoInvoker.invoke(
command=f"apt-get install -y --no-install-recommends {' '.join(packages)}",
exception_class=AptGetInstaller.AptGetUpdateFailed,
)

finally:
if remove_ppas_on_completion:
for ppa in normalized_ppas:
SudoInvoker.invoke(
command=f"add-apt-repository -y --remove {ppa}",
exception_class=AptGetInstaller.RemovePPAsFailed,
)

if remove_cache_on_completion:
SudoInvoker.invoke(
command="rm -rf /var/lib/apt/lists/*",
exception_class=AptGetInstaller.CleanUpFailed,
)
21 changes: 11 additions & 10 deletions dcontainer/cli/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import typer


logger = logging.getLogger(__name__)

app = typer.Typer(pretty_exceptions_show_locals=False, pretty_exceptions_short=False)
Expand All @@ -14,20 +13,22 @@

@app.command("devcontainer-feature")
def generate_command(
feature_definition: pathlib.Path,
output_dir: pathlib.Path,
release_version: Optional[str] = None
feature_definition: pathlib.Path,
output_dir: pathlib.Path,
release_version: Optional[str] = None,
) -> None:
try:
from dcontainer.devcontainer.feature_generation.oci_feature_generator import OCIFeatureGenerator
from dcontainer.devcontainer.feature_generation.oci_feature_generator import (
OCIFeatureGenerator,
)
except ImportError as e:
logger.error(
"Some imports required for feature generation are missing.\nMake sure you have included the generate extras during installation.\n eg. 'pip install dcontainer[generate]'"
)
raise typer.Exit(code=1) from e

OCIFeatureGenerator.generate(feature_definition=feature_definition.as_posix(),
output_dir=output_dir.as_posix(),
release_version=release_version)


OCIFeatureGenerator.generate(
feature_definition=feature_definition.as_posix(),
output_dir=output_dir.as_posix(),
release_version=release_version,
)
47 changes: 36 additions & 11 deletions dcontainer/cli/install.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from typing import List, Optional, Dict

from typing import Dict, List, Optional
from dcontainer.devcontainer.oci_feature_installer import OCIFeatureInstaller
from dcontainer.apt_get.apt_get_installer import AptGetInstaller
import typer

logger = logging.getLogger(__name__)
Expand All @@ -21,13 +22,13 @@ def _validate_args(value: Optional[List[str]]):

@app.command("devcontainer-feature")
def install_devcontainer_feature(
feature: str,
option: Optional[List[str]] = typer.Option(None, callback=_validate_args),
remote_user: Optional[str] = typer.Option(None, callback=_validate_args),
env: Optional[List[str]] = typer.Option(None, callback=_validate_args),
verbose: bool = False,
feature: str,
option: Optional[List[str]] = typer.Option(None, callback=_validate_args),
remote_user: Optional[str] = typer.Option(None, callback=_validate_args),
env: Optional[List[str]] = typer.Option(None, callback=_validate_args),
verbose: bool = False,
) -> None:
from dcontainer.devcontainer.oci_feature_installer import OCIFeatureInstaller


def _key_val_arg_to_dict(args: Optional[List[str]]) -> Dict[str, str]:
if args is None:
Expand All @@ -37,7 +38,7 @@ def _key_val_arg_to_dict(args: Optional[List[str]]) -> Dict[str, str]:
for single_arg in args:
single_arg = _strip_if_wrapped_around(single_arg, '"')
arg_name = single_arg.split("=")[0]
arg_value = single_arg[len(arg_name) + 1:]
arg_value = single_arg[len(arg_name) + 1 :]
arg_value = _strip_if_wrapped_around(arg_value, '"')
args_dict[arg_name] = arg_value
return args_dict
Expand All @@ -55,5 +56,29 @@ def _strip_if_wrapped_around(value: str, char: str) -> str:
options_dict = _key_val_arg_to_dict(option)
envs_dict = _key_val_arg_to_dict(env)

OCIFeatureInstaller.install(feature_ref=feature, envs=envs_dict, options=options_dict, remote_user=remote_user,
verbose=verbose)
OCIFeatureInstaller.install(
feature_ref=feature,
envs=envs_dict,
options=options_dict,
remote_user=remote_user,
verbose=verbose,
)


@app.command("apt-get")
def install_apt_get_packages(
package: List[str],
ppa: Optional[List[str]] = typer.Option(None),
force_ppas_on_non_ubuntu: bool = True,
remove_ppas_on_completion: bool = True,
remove_cache_on_completion: bool = True,
) -> None:


AptGetInstaller.install(
packages=package,
ppas=ppa,
force_ppas_on_non_ubuntu=force_ppas_on_non_ubuntu,
remove_ppas_on_completion=remove_ppas_on_completion,
remove_cache_on_completion=remove_cache_on_completion,
)
19 changes: 14 additions & 5 deletions dcontainer/devcontainer/feature_generation/dir_models/src_dir.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
from easyfs import Directory
from typing import Optional

from dcontainer.devcontainer.feature_generation.file_models.dependencies_sh import DependenciesSH
from easyfs import Directory

from dcontainer.devcontainer.feature_generation.file_models.dependencies_sh import (
DependenciesSH,
)
from dcontainer.devcontainer.feature_generation.file_models.devcontainer_feature_json import (
DevcontainerFeatureJson,
)
from dcontainer.devcontainer.feature_generation.file_models.install_command_sh import InstallCommandSH
from dcontainer.devcontainer.feature_generation.file_models.install_command_sh import (
InstallCommandSH,
)
from dcontainer.devcontainer.feature_generation.file_models.install_sh import InstallSH
from dcontainer.devcontainer.models.devcontainer_feature_definition import FeatureDefinition
from dcontainer.devcontainer.models.devcontainer_feature_definition import (
FeatureDefinition,
)


class SrcDir(Directory):
@classmethod
def from_definition_model(cls, definition_model: FeatureDefinition, release_version: Optional[str] = None) -> "Directory":
def from_definition_model(
cls, definition_model: FeatureDefinition, release_version: Optional[str] = None
) -> "Directory":
feature_id = definition_model.id

virtual_dir = {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from easyfs import Directory

from dcontainer.devcontainer.feature_generation.file_models.scenarios_json import ScenariosJson
from dcontainer.devcontainer.feature_generation.file_models.scenarios_json import (
ScenariosJson,
)
from dcontainer.devcontainer.feature_generation.file_models.test_sh import TestSH
from dcontainer.devcontainer.models.devcontainer_feature_definition import (
FeatureDefinition,
Expand Down
Loading

0 comments on commit cc9b6ee

Please sign in to comment.