Skip to content
Draft
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
97 changes: 43 additions & 54 deletions builder.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ set +u

DOCKER_TIMEOUT=20
DOCKER_PID=-1
DOCKER_BUILDX_BUILDER="ha-builder"
DOCKER_HUB=
DOCKER_HUB_CHECK=false
DOCKER_CACHE=true
Expand Down Expand Up @@ -207,6 +208,16 @@ function stop_docker() {
}


function create_buildx_builder() {
bashio::log.info "Creating buildx builder: ${DOCKER_BUILDX_BUILDER}..."
if ! docker inspect "${DOCKER_BUILDX_BUILDER}" >/dev/null 2>&1; then
if ! docker buildx create --name "${DOCKER_BUILDX_BUILDER}" >/dev/null; then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I was wondering why this is necessary, and now realized this is required so that the docker-container BuildKit driver is used. Maybe for clarity lets specify this explicitly here (use --driver docker-container).

bashio::exit.nok "Failed to create buildx builder"
fi
fi
}


function run_build() {
local build_dir=$1
local repository=$2
Expand All @@ -218,7 +229,6 @@ function run_build() {
local docker_tags=("${!8}")
local shadow_repository=${9}

local push_images=()
local cache_tag="latest"
local metadata
local release="${version}"
Expand Down Expand Up @@ -319,14 +329,41 @@ function run_build() {
dockerfile="${build_dir}/Dockerfile.${build_arch}"
fi

# Tag latest
if bashio::var.true "${DOCKER_LATEST}"; then
docker_tags+=("latest")
fi

# Add additional tags
for tag_image in "${ADDITIONAL_TAGS[@]}"; do
docker_tags+=("${tag_image}")
done

# Use shadow repository
if bashio::var.has_value "${shadow_repository}"; then
shadow_tags=("${shadow_repository}/${image}:${version}")
for tag_image in "${docker_tags[@]}"; do
shadow_tags+=("${shadow_repository}/${image}:${tag_image}")
done
docker_tags+=("${shadow_tags[@]}")
fi

# Tag images
for tag_image in "${docker_tags[@]}"; do
docker_cli+=(--tag "${tag_image}")
done

# Build image
bashio::log.info "Run build for ${repository}/${image}:${version} with platform ${docker_platform}"
${docker_wrapper} docker buildx build --pull --tag "${repository}/${image}:${version}" \
--builder "${DOCKER_BUILDX_BUILDER}" \
--platform "${docker_platform}" \
--build-arg "BUILD_FROM=${build_from}" \
--build-arg "BUILD_VERSION=${version}" \
--build-arg "BUILD_ARCH=${build_arch}" \
--file "${dockerfile}" \
--output "type=image,oci-mediatypes=true,compression=zstd,push=${DOCKER_PUSH}" \
--load \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we push BuildKit, is that even necessary? Maybe it is because of cosign, but cosign has another problem, see below.

"${docker_cli[@]}" \
"${build_dir}"

Expand All @@ -337,61 +374,12 @@ function run_build() {
return 0
fi

push_images+=("${repository}/${image}:${version}")
bashio::log.info "Finish build for ${repository}/${image}:${version}"

# Tag latest
if bashio::var.true "${DOCKER_LATEST}"; then
docker_tags+=("latest")
fi

# Add additional tags
for tag_image in "${ADDITIONAL_TAGS[@]}"; do
docker_tags+=("${tag_image}")
done

# Tag images
for tag_image in "${docker_tags[@]}"; do
bashio::log.info "Create image tag: ${tag_image}"
docker tag "${repository}/${image}:${version}" "${repository}/${image}:${tag_image}"
push_images+=("${repository}/${image}:${tag_image}")
done

# Use shaddow repository
if bashio::var.has_value "${shadow_repository}"; then
bashio::log.info "Generate repository shadow images"
docker tag "${repository}/${image}:${version}" "${shadow_repository}/${image}:${version}"
for tag_image in "${docker_tags[@]}"; do
bashio::log.info "Create shadow-image tag: ${shadow_repository}/${image}:${tag_image}"
docker tag "${repository}/${image}:${version}" "${shadow_repository}/${image}:${tag_image}"
push_images+=("${shadow_repository}/${image}:${tag_image}")
done
push_images+=("${shadow_repository}/${image}:${version}")
fi

# Push images
if bashio::var.true "${DOCKER_PUSH}"; then
for i in "${push_images[@]}"; do
for j in {1..3}; do
bashio::log.info "Start upload of ${i} (attempt #${j}/3)"
if docker push "${i}" > /dev/null 2>&1; then
bashio::log.info "Upload succeeded on attempt #${j}"
break
fi
if [[ "${j}" == "3" ]]; then
bashio::exit.nok "Upload failed on attempt #${j}"
else
bashio::log.warning "Upload failed on attempt #${j}"
sleep 30
fi
done
done

# Singing image (cosign)
if bashio::var.true "${COSIGN}"; then
image_digest=$(docker inspect --format='{{index .RepoDigests 0}}' "${repository}/${image}:${version}")
cosign_sign "${image_digest}"
fi
# Sign image (cosign)
if bashio::var.true "${DOCKER_PUSH}" && bashio::var.true "${COSIGN}"; then
image_digest=$(docker inspect --format='{{index .RepoDigests 0}}' "${repository}/${image}:${version}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stops working with:

template parsing error: template: :1:2: executing "" at <index .RepoDigests 0>: error calling index: reflect: slice index out of range

It does make sense, since we load the image directly from the builder, with that Docker does not know the resulting digest in the repository...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that this is really a limitation of the graph driver Docker container backend. Using the containerd snapshotter seems to retain the digest

By default the containerd snapshotter is not enabled, but this GitHub Action would allow to enable it:
https://github.com/depot/use-containerd-snapshotter-action

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Researching a bit more into it, it seems that containerd snapshotter also supports storing the layers as zstd. From what I understand it will still required buildx to actually create the layer in zstd format, but the import into Docker storage will retain the compression. With that regular docker push can be used to upload zstd compressed layers. It would also make it viable for hassio-addons (refs hassio-addons/workflows#220 (comment)).

cosign_sign "${image_digest}"
fi
}

Expand Down Expand Up @@ -965,6 +953,7 @@ mkdir -p /data
# Setup docker env
init_crosscompile
start_docker
create_buildx_builder

# Load external repository
if [ -n "$GIT_REPOSITORY" ]; then
Expand Down