Last active
July 21, 2020 21:01
-
-
Save mdelillo/f587716e78780375820759a24b496df2 to your computer and use it in GitHub Desktop.
Create a builder from specified buildpacks, buildpackages, and mixins
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -euo pipefail | |
timestamp="$(date +%s)" | |
builder_name="builder-${timestamp}" | |
mixins="" | |
buildpackages=() | |
buildpacks=() | |
usage() { | |
cat <<-EOF | |
Usage: | |
$0 [OPTIONS] | |
Options: | |
-b /path/to/buildpack1.tgz -b https://url/to/buildpack2.tgz ... | |
-c /path/to/buildpackage1.cnb -c gcr.io/path/to/buildpackage2 ... | |
-m <mixin1,build:mixin2,run:mixin3> | |
-n <builder-name> | |
EOF | |
} | |
parse_opts() { | |
while getopts ":b:c:hm:n:" opt; do | |
case ${opt} in | |
b) | |
buildpacks+=("${OPTARG}") | |
;; | |
c) | |
buildpackages+=("${OPTARG}") | |
;; | |
h) | |
usage | |
exit 0 | |
;; | |
m) | |
mixins="${OPTARG//,/ }" | |
;; | |
n) | |
builder_name="${OPTARG}" | |
;; | |
\?) | |
echo "Invalid option: -${OPTARG}" >&2 | |
usage | |
exit 1 | |
;; | |
:) | |
echo "Option -${OPTARG} requires an argument." >&2 | |
usage | |
exit 1 | |
;; | |
esac | |
done | |
} | |
get_image_packages() { | |
image="$1" | |
packages="" | |
for mixin in ${mixins}; do | |
if [[ ${mixin} = *set=* ]]; then | |
continue | |
fi | |
if [[ ${mixin} = ${image}:* ]]; then | |
packages="${packages} ${mixin#"${image}:"}" | |
elif [[ ${mixin} != build:* ]] && [[ ${mixin} != run:* ]]; then | |
packages="${packages} ${mixin}" | |
fi | |
done | |
echo -n "${packages}" | |
} | |
get_mixin_labels() { | |
image="$1" | |
all_build_packages="$(docker run --rm -it "${builder_name}-build" dpkg-query -f '${Package}\n' -W | sort -u | tr -d '\r')" | |
all_run_packages="$(docker run --rm -it "${builder_name}-run" dpkg-query -f '${Package}\n' -W | sort -u | tr -d '\r')" | |
shared_packages="$(comm -12 <(echo "${all_build_packages}") <(echo "${all_run_packages}"))" | |
if [[ $image == "build" ]]; then | |
exclusive_packages="$(comm -23 <(echo "${all_build_packages}") <(echo "${all_run_packages}"))" | |
elif [[ $image == "run" ]]; then | |
exclusive_packages="$(comm -13 <(echo "${all_build_packages}") <(echo "${all_run_packages}"))" | |
else | |
>&2 echo "Cannot get mixin labels for image $image" | |
exit 1 | |
fi | |
additional_mixins="" | |
for mixin in ${mixins}; do | |
if [[ ${mixin} = set=* ]] || [[ ${mixin} = ${image}:set=* ]]; then | |
additional_mixins="${additional_mixins}\n${mixin#"${image}:"}" | |
fi | |
done | |
image_mixins="$(cat <<EOF | |
${shared_packages} | |
$([[ -n "${shared_packages}" ]] && echo "${shared_packages}" | sed "s/^/${image}:/") | |
$([[ -n "${exclusive_packages}" ]] && echo "${exclusive_packages}" | sed "s/^/${image}:/") | |
$([[ -n "${additional_mixins}" ]] && echo -e "${additional_mixins}") | |
EOF | |
)" | |
echo "${image_mixins}" | grep -v '^$' | jq -cnR '[inputs | select(length>0)]' | |
} | |
build_build_image() { | |
packages="$1" | |
dockerfile_dir="$(mktemp -d)" | |
cat >"${dockerfile_dir}/Dockerfile" <<-EOF | |
FROM ubuntu:bionic | |
RUN apt-get -qqy update && \ | |
apt-get -qqy install \ | |
build-essential \ | |
ca-certificates \ | |
curl \ | |
git \ | |
jq \ | |
libssl1.1 \ | |
openssl \ | |
xz-utils \ | |
zlib1g-dev \ | |
${build_image_packages} \ | |
&& rm -rf /var/lib/apt/lists/* | |
RUN curl -sL -o /usr/local/bin/yj https://github.com/sclevine/yj/releases/latest/download/yj-linux \ | |
&& chmod +x /usr/local/bin/yj | |
EOF | |
docker build -t "${builder_name}-build" "${dockerfile_dir}" | |
} | |
build_run_image() { | |
packages="$1" | |
dockerfile_dir="$(mktemp -d)" | |
cat >"${dockerfile_dir}/Dockerfile" <<-EOF | |
FROM ubuntu:bionic | |
RUN apt-get -qqy update && \ | |
apt-get -qqy install \ | |
ca-certificates \ | |
libssl1.1 \ | |
openssl \ | |
zlib1g \ | |
${run_image_packages} \ | |
&& rm -rf /var/lib/apt/lists/* | |
EOF | |
docker build -t "${builder_name}-run" "${dockerfile_dir}" | |
} | |
build_build_cnb_image() { | |
mixins_label="$1" | |
dockerfile_dir="$(mktemp -d)" | |
cat >"${dockerfile_dir}/Dockerfile" <<-EOF | |
FROM ${builder_name}-build | |
RUN groupadd cnb --gid 1000 && \ | |
useradd --uid 1000 --gid 1000 -m -s /bin/bash cnb | |
ENV CNB_USER_ID=1000 | |
ENV CNB_GROUP_ID=1000 | |
ENV CNB_STACK_ID=io.buildpacks.stacks.bionic | |
USER 1000:1000 | |
LABEL io.buildpacks.stack.id=io.buildpacks.stacks.bionic | |
LABEL io.buildpacks.stack.mixins="$(echo "${mixins_label}" | sed 's/"/\\"/g')" | |
EOF | |
docker build -t "${builder_name}-build-cnb" "${dockerfile_dir}" | |
} | |
build_run_cnb_image() { | |
mixins_label="$1" | |
dockerfile_dir="$(mktemp -d)" | |
cat >"${dockerfile_dir}/Dockerfile" <<-EOF | |
FROM ${builder_name}-run | |
RUN groupadd cnb --gid 1000 && \ | |
useradd --uid 1000 --gid 1000 -m -s /bin/bash cnb | |
USER 1000:1000 | |
LABEL io.buildpacks.stack.id=io.buildpacks.stacks.bionic | |
LABEL io.buildpacks.stack.mixins="$(echo "${mixins_label}" | sed 's/"/\\"/g')" | |
EOF | |
docker build -t "${builder_name}-run-cnb" "${dockerfile_dir}" | |
} | |
build_builder() { | |
builder_toml="$(mktemp)" | |
cat >"${builder_toml}" <<-EOF | |
description = "${builder_name}" | |
[stack] | |
id = "io.buildpacks.stacks.bionic" | |
run-image = "${builder_name}-run-cnb" | |
build-image = "${builder_name}-build-cnb" | |
EOF | |
if [[ ${#buildpackages[@]} -gt 0 ]]; then | |
for buildpackage in "${buildpackages[@]}"; do | |
if [[ -f "${buildpackage}" ]]; then | |
buildpackage_path="$(cd "$(dirname "${buildpackage}")" && pwd)/$(basename "${buildpackage}")" | |
manifest_filename="/blobs/$(tar -O -xf "${buildpackage_path}" /index.json 2>/dev/null | jq -r .manifests[0].digest | tr ':' '/')" | |
config_filename="/blobs/$(tar -O -xf "${buildpackage_path}" "${manifest_filename}" | jq -r .config.digest | tr ':' '/')" | |
metadata="$(tar -O -xf "${buildpackage_path}" "${config_filename}" | jq -r '.config.Labels."io.buildpacks.buildpackage.metadata"')" | |
meta_buildpack_id="$(jq -r .id <<< "${metadata}")" | |
cat >>"${builder_toml}" <<-EOF | |
[[buildpacks]] | |
id = "${meta_buildpack_id}" | |
uri = "${buildpackage_path}" | |
EOF | |
elif docker pull "${buildpackage}"; then | |
meta_buildpack_id="$(docker inspect "${buildpackage}" | jq -r '.[0].Config.Labels."io.buildpacks.buildpackage.metadata"' | jq -r .id)" | |
cat >>"${builder_toml}" <<-EOF | |
[[buildpacks]] | |
id = "${meta_buildpack_id}" | |
image = "${buildpackage}" | |
EOF | |
else | |
echo "Unknown buildpackage '${buildpackage}'" | |
exit 1 | |
fi | |
cat >>"${builder_toml}" <<-EOF | |
[[order]] | |
[[order.group]] | |
id = "${meta_buildpack_id}" | |
EOF | |
done | |
fi | |
if [[ ${#buildpacks[@]} -gt 0 ]]; then | |
for buildpack in "${buildpacks[@]}"; do | |
if [[ -f "${buildpack}" ]]; then | |
buildpack_path="$(cd "$(dirname "${buildpack}")" && pwd)/$(basename "${buildpack}")" | |
buildpack_id="$(tar -O -xf "${buildpack_path}" buildpack.toml | yj -tj | jq -r .buildpack.id)" | |
elif curl -fsL "${buildpack}" >/dev/null 2>&1; then | |
buildpack_path="${buildpack}" | |
buildpack_id="$(curl -sL "${buildpack}" | tar -O -x buildpack.toml | yj -tj | jq -r .buildpack.id)" | |
else | |
echo "Unknown buildpack '${buildpack}'" | |
exit 1 | |
fi | |
cat >>"${builder_toml}" <<-EOF | |
[[buildpacks]] | |
id = "${buildpack_id}" | |
uri = "${buildpack_path}" | |
[[order]] | |
[[order.group]] | |
id = "${buildpack_id}" | |
EOF | |
done | |
fi | |
pack create-builder "${builder_name}" --config "${builder_toml}" | |
echo "######## builder.toml:" | |
cat "${builder_toml}" | |
} | |
main() { | |
parse_opts "$@" | |
build_image_packages="$(get_image_packages "build")" | |
run_image_packages="$(get_image_packages "run")" | |
build_build_image "${build_image_packages}" | |
build_run_image "${run_image_packages}" | |
build_image_mixin_labels="$(get_mixin_labels "build")" | |
run_image_mixin_labels="$(get_mixin_labels "run")" | |
build_build_cnb_image "${build_image_mixin_labels}" | |
build_run_cnb_image "${run_image_mixin_labels}" | |
build_builder | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment