Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed dependency on docker #21

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
12 changes: 11 additions & 1 deletion toltec/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ def main() -> int:
(default: [current directory]/dist)""",
)

parser.add_argument(
"-s",
"--source-dir",
metavar="DIR",
default=None,
help="""path to the source directory
(optional: when specified, a local build is performed instead of fetching
sources)""",
)

parser.add_argument(
"-a",
"--arch-name",
Expand Down Expand Up @@ -80,7 +90,7 @@ def main() -> int:

recipe_bundle = parse_recipe(args.recipe_dir)

with Builder(args.work_dir, args.dist_dir) as builder:
with Builder(args.work_dir, args.dist_dir, args.source_dir) as builder:
if args.hook:
for ident in args.hook:
if ident and ident[0] in (".", "/"):
Expand Down
54 changes: 0 additions & 54 deletions toltec/bash.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import logging
from collections import deque
from typing import Deque, Dict, Generator, List, Optional, Tuple, Union
from docker.client import DockerClient

AssociativeArray = Dict[str, str]
IndexedArray = List[Optional[str]]
Expand Down Expand Up @@ -363,59 +362,6 @@ def run_script(variables: Variables, script: str) -> LogGenerator:
raise ScriptError(f"Script exited with code {process.returncode}")


def run_script_in_container(
docker: DockerClient,
image: str,
mounts: List,
variables: Variables,
script: str,
) -> LogGenerator:
"""
Run a Bash script inside a Docker container and stream its output.

:param docker: Docker client
:param image: image to use for the new container
:param mounts: paths to mount in the container
:param variables: Bash variables to set before running the script
:param script: Bash script to execute
:returns: generator yielding output lines from the script
:raises ScriptError: if the script exits with a non-zero code
"""
container = docker.containers.run(
image,
mounts=mounts,
command=[
"/usr/bin/env",
"bash",
"-c",
"\n".join(
(
"set -euo pipefail",
put_variables(variables),
"script() {",
script,
"}",
"script",
)
),
],
detach=True,
security_opt=["label=disable"],
)

try:
for line in container.logs(stream=True):
if line:
yield line.decode().strip()

result = container.wait()

if result["StatusCode"] != 0:
raise ScriptError(f"Script exited with code {result['StatusCode']}")
finally:
container.remove()


def pipe_logs(
logger: logging.Logger,
logs: LogGenerator,
Expand Down
62 changes: 17 additions & 45 deletions toltec/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import os
import logging
import textwrap
import docker
import requests
from . import bash, util, ipk
from .recipe import RecipeBundle, Recipe, Package
Expand All @@ -28,10 +27,9 @@ class Builder: # pylint: disable=too-few-public-methods
# Detect non-local paths
URL_REGEX = re.compile(r"[a-z]+://")

# Prefix for all Toltec Docker images
IMAGE_PREFIX = "ghcr.io/toltec-dev/"

def __init__(self, work_dir: str, dist_dir: str) -> None:
def __init__(
self, work_dir: str, dist_dir: str, source_dir: Optional[str]
) -> None:
"""
Create a builder helper.

Expand All @@ -40,15 +38,7 @@ def __init__(self, work_dir: str, dist_dir: str) -> None:
"""
self.work_dir = work_dir
self.dist_dir = dist_dir

try:
self.docker = docker.from_env()
except docker.errors.DockerException as err:
raise BuildError(
"Unable to connect to the Docker daemon. \
Please check that the service is running and that you have the necessary \
permissions."
) from err
self.source_dir = source_dir

def __enter__(self) -> "Builder":
return self
Expand All @@ -59,7 +49,7 @@ def __exit__(
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
self.docker.close()
pass

@util.hook
def post_parse(self, recipe: Recipe) -> None:
Expand Down Expand Up @@ -164,13 +154,13 @@ def _make_arch(
packages: Optional[List[Package]] = None,
) -> bool:
self.post_parse(recipe)

src_dir = os.path.join(build_dir, "src")
os.makedirs(src_dir, exist_ok=True)

self._fetch_sources(recipe, src_dir)
self.post_fetch_sources(recipe, src_dir)

if self.source_dir:
src_dir = self.source_dir
else:
src_dir = os.path.join(build_dir, "src")
os.makedirs(src_dir, exist_ok=True)
self._fetch_sources(recipe, src_dir)
self.post_fetch_sources(recipe, src_dir)
self._prepare(recipe, src_dir)
self.post_prepare(recipe, src_dir)

Expand Down Expand Up @@ -274,8 +264,6 @@ def _build(self, recipe: Recipe, src_dir: str) -> None:
for filename in util.list_tree(src_dir):
os.utime(filename, (epoch, epoch))

mount_src = "/src"
repo_src = "/repo"
uid = os.getuid()
pre_script: List[str] = []

Expand Down Expand Up @@ -331,33 +319,17 @@ def _build(self, recipe: Recipe, src_dir: str) -> None:
" -- " + " ".join(host_deps),
)
)

logs = bash.run_script_in_container(
self.docker,
image=self.IMAGE_PREFIX + recipe.image,
mounts=[
docker.types.Mount(
type="bind",
source=os.path.abspath(src_dir),
target=mount_src,
),
docker.types.Mount(
type="bind",
source=os.path.abspath(self.dist_dir),
target=repo_src,
),
],
variables={
"srcdir": mount_src,
},
logs = bash.run_script(
script="\n".join(
(
f'cd "{src_dir}"',
*pre_script,
f'cd "{mount_src}"',
recipe.build,
f'chown -R {uid}:{uid} "{mount_src}"',
)
),
variables={
"srcdir": src_dir,
},
)
bash.pipe_logs(logger, logs, "build()")

Expand Down
23 changes: 11 additions & 12 deletions toltec/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
IO,
List,
Optional,
Protocol,
# Protocol,
Type,
Union,
)
Expand Down Expand Up @@ -302,6 +302,8 @@ def check_directory(path: str, message: str) -> bool:
try:
os.mkdir(path)
except FileExistsError:
if not os.listdir(path):
return True
ans = query_user(
message,
default="c",
Expand Down Expand Up @@ -343,18 +345,15 @@ def list_tree(root: str) -> List[str]:
HookTrigger = Callable[..., None]
HookListener = Callable[..., None]

# Protocol is not available in python 3.7 (Debian buster)
Hook = Any
# class Hook(Protocol): # pylint:disable=too-few-public-methods
# """Protocol for hooks."""

class Hook(Protocol): # pylint:disable=too-few-public-methods
"""Protocol for hooks."""

@staticmethod
def register(new_listener: HookListener) -> None:
"""Add a new listener to this hook."""
...

# Invoke all listeners for this hook
__call__: HookTrigger

# @staticmethod
# def register(new_listener: HookListener) -> None:
# """Add a new listener to this hook."""
# ...

def hook(func: HookTrigger) -> Hook:
"""
Expand Down
1 change: 1 addition & 0 deletions toltecmk
59 changes: 59 additions & 0 deletions toltecmk-contained
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
# Copyright (c) 2021 The Toltec Contributors
# SPDX-License-Identifier: MIT

import argparse
import os
import subprocess
from pathlib import Path

from toltec import parse_recipe
from toltecmk import make_argparser

# Prefix for all Toltec Docker images
IMAGE_PREFIX = "ghcr.io/toltec-dev/"

args = make_argparser().parse_args()

recipe_bundle = parse_recipe(args.recipe_dir)
# Don't recipes in a bundle share most information?
recipe = next(iter(recipe_bundle.values()))

image = IMAGE_PREFIX + recipe.image

toltecmk_dir = str(Path(__file__).resolve().parent)

mounts = [ (args.recipe_dir, '/recipe'),
(args.work_dir, '/build'),
(args.dist_dir, '/pkg'),
(toltecmk_dir, '/toltecmk'),
]

for m in mounts:
if not os.path.exists(m[0]):
os.makedirs(m[0])

if args.source_dir:
mounts.append((args.source_dir, '/src'))

cmd = ['podman', 'run', '-it', '--rm']
for m in mounts:
cmd.append('-v')
cmd.append(':'.join(m))
cmd.append(image)
cmd += ['bash', '-c']
# currently required by toltecmk and missing in images
internal_cmd = []
internal_cmd += ['apt update && apt install -yq python3-dateutil python3-requests && ']
internal_cmd += ['/toltecmk/toltecmk',
'-w', '/build',
'-d', '/pkg',
'/recipe']
if args.source_dir: internal_cmd += ['-s', '/src']
if args.arch_name: internal_cmd += ['-a', args.arch_name]
if args.package_name: internal_cmd += ['-p', args.package_name]

cmd.append(' '.join(internal_cmd))
print(' '.join('"' + c + '"' if ' ' in c else c for c in cmd))
subprocess.call(cmd)

Loading