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

add build arg and build context args #167

Merged
merged 4 commits into from
Dec 30, 2023
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
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,27 @@ Skipper can serve as your primary tool for your daily development tasks:
--build-container-tag Tag of the build container
--build-container-net Network to connect the build container (default: net=host)
--env-file Environment variables file/s to pass to the container
--build-arg Set build-time variables for the container
--build-context Additional build contexts when running the build command, give them a name, and then access them inside a Dockerfile
--help Show this message and exit.
```

### [Build context explained](https://www.docker.com/blog/dockerfiles-now-support-multiple-build-contexts/)
Skipper allows you to add additional build contexts when running the build command, give them a name, and then access them inside a Dockerfile.
The build context can be one of the following:
* Local directory – e.g. `--build-context project2=../path/to/project2/src`
* Git repository – e.g. `--build-context qemu-src=https://github.com/qemu/qemu.git`
* HTTP URL to a tarball – e.g. `--build-context src=https://example.org/releases/src.tar`
* Docker image – Define with a `docker-image://` prefix, e.g. `--build-context alpine=docker-image://alpine:3.15`

On the Dockerfile side, you can reference the build context on all commands that accept the “from” parameter. Here’s how that might look:

```dockerfile
FROM [name]
COPY --from=[name] ...
RUN --mount=from=[name] …
```

### Build
As a convention, skipper infers the docker images from the Dockerfiles in the top directory of your repository. For example, assuming that there are 3 Dockerfile in the top directory of the repository:
```
Expand Down Expand Up @@ -154,6 +172,16 @@ build-container-image: development
build-container-tag: latest
container-context: /path/to/context/dir

build-arg:
- VAR1=value1
- VAR2=value2

build-context:
- context1=/path/to/context/dir # Local directory
- qemu-src=https://github.com/qemu/qemu.git # Remote git repository
- src=https://example.org/releases/src.tar # Remote tar file
- alpine=docker-image://alpine:3.15 # Remote docker image

make:
makefile: Makefile.arm32
containers:
Expand All @@ -164,6 +192,14 @@ env:
env_file: path/to/env_file.env
```

```yaml
# Use the git revision as the build container tag
# Allows to use the same build container unless the git revision changes
# This is useful when using a CI system that caches the build container
# Remember to commit if you changing the build container
khizunov marked this conversation as resolved.
Show resolved Hide resolved
build-container-tag: 'git:revision'
```

Using the above configuration file, we now can run a simplified version of the make command described above:
```bash
skipper make tests
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PyYAML>=3.11
click==6.7
click>=6.7
requests>=2.6.0
tabulate>=0.7.5
six>=1.10.0
Expand Down
10 changes: 5 additions & 5 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
[metadata]
name = strato-skipper
author = Adir Gabai
author-email = [email protected]
home-page = http://github.com/Stratoscale/skipper
author_email = [email protected]
home_page = http://github.com/Stratoscale/skipper
summary = Easily dockerize your Git repository
license = Apache-2
long_description = file: README.md
long_description_content_type = text/markdown

requires-dist = setuptools
requires-python = >=3
requires_dist = setuptools
requires_python = >=3


[files]
Expand All @@ -21,4 +21,4 @@ console_scripts =
skipper = skipper.main:main

[pep8]
max-line-length=145
max_line_length=145
192 changes: 192 additions & 0 deletions skipper/builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
from dataclasses import dataclass
from logging import Logger
from typing import Callable

from skipper import utils

DOCKER_TAG_FOR_CACHE = "cache"


class Image:
"""
A class to represent an image with attributes like registry, name, tag, namespace.
"""

def __init__(self, name, tag, dockerfile=None, registry=None, namespace=None):
"""
Constructs all the necessary attributes for the Image object.

:param registry: Registry for the image
:param name: Name of the image
:param tag: Tag for the image
:param namespace: Namespace for the registry
"""
if not name:
raise ValueError("Image name is required")

self.name = name
self.tag = tag
self.registry = registry
self.namespace = namespace
self.__dockerfile = dockerfile
self.__cache_fqdn = None
self.__fqdn = None

def __str__(self):
return self.fqdn

@property
def local(self):
"""
Creates a string that represents the local image.

:return: Image in name:tag format
"""
if self.tag:
return self.name + ":" + self.tag
return self.name

@property
def cache_fqdn(self):
"""
Generates a Fully Qualified Domain Name for the cached image.

:return: Cached image Fully Qualified Domain Name
"""
if not self.__cache_fqdn:
self.__cache_fqdn = utils.generate_fqdn_image(
self.registry, self.namespace, self.name, DOCKER_TAG_FOR_CACHE
)
return self.__cache_fqdn

@property
def fqdn(self):
"""
Generates a Fully Qualified Domain Name for the image.

:return: image Fully Qualified Domain Name
"""
if not self.__fqdn:
self.__fqdn = utils.generate_fqdn_image(
self.registry, self.namespace, self.name, self.tag
)
return self.__fqdn

@property
def dockerfile(self):
"""
Returns the Dockerfile for the image.

:return: Dockerfile for the image
"""
if not self.__dockerfile:
self.__dockerfile = utils.image_to_dockerfile(self.name)
return self.__dockerfile

@classmethod
def from_context_obj(cls, ctx_obj):
"""
Creates an instance of Image from a given context.

:param ctx_obj: Click context object
:return: An instance of Image
"""
if ctx_obj is None:
return None

return cls(
name=ctx_obj.get("build_container_image"),
tag=ctx_obj.get("build_container_tag"),
registry=ctx_obj.get("registry"),
)


@dataclass
class BuildOptions:
"""
A class to encapsulate all the build options needed to create Docker image.
"""

def __init__(
self,
image: Image,
container_context,
build_contexts=None,
build_args=None,
use_cache=False,
):
"""
Constructs all the necessary attributes for the build options.

:param image: image details as an instance of Image
:param container_context: Context for the build
:param build_contexts: Build contexts to add to build
:param build_args: Arguments to pass to build
:param use_cache: Boolean indicating if cache should be used
"""
self.image = image
self.container_context = container_context
self.build_contexts = [ctx for ctx in build_contexts if ctx] if build_contexts else []
self.build_args = [arg for arg in build_args if arg] if build_args else []
self.use_cache = use_cache

@classmethod
def from_context_obj(cls, ctx_obj):
"""
Creates an instance of BuildOptions from a given context.

:param ctx_obj: Click context object
:return: An instance of BuildOptions
"""
if ctx_obj is None:
return None

return cls(
image=Image.from_context_obj(ctx_obj),
container_context=ctx_obj.get("container_context"),
build_contexts=ctx_obj.get("build_contexts"),
build_args=ctx_obj.get("build_args"),
use_cache=ctx_obj.get("use_cache"),
)


def build(options: BuildOptions, runner: Callable, logger: Logger) -> int:
"""
Builds a image based on given build options and runner function.

:param options: Build options as an instance of BuildOptions
:param runner: Callable that runs the Docker commands
:param logger: Logger instance
:return: A return code representing the success or failure of the build
"""
cmd = ["build", "--network=host"]

for arg in options.build_args:
cmd += ["--build-arg", arg]

for build_ctx in options.build_contexts:
cmd += ["--build-context", build_ctx]

cmd += [
"-f",
options.image.dockerfile,
"-t",
options.image.local,
options.container_context or ".",
]

if options.use_cache:
runner(["pull", options.image.cache_fqdn])
cmd.extend(["--cache-from", options.image.cache_fqdn])

ret = runner(cmd)

if ret != 0:
logger.error("Failed to build image: %s", options.image)
return ret

if options.use_cache:
runner(["tag", options.image.name, options.image.cache_fqdn])
runner(["push", options.image.cache_fqdn])

return 0
Loading
Loading